如何结合继承实现观察者模式?
How do I realize the Observer pattern in conjunction with inheritance?
我有一个 class 管理配置文件 SettingsController
,它允许注册一个 SettingsClient
(一个纯虚函数,无成员)。 SettingsClient
将在相关配置条目更改时收到通知,以便刷新。
现在我有一个 RepositoryBase
,它需要一些配置条目,因此继承 SettingsClient
并在 SettingsController
和一个具体的存储库 ConcreteRepository
注册,它也需要一些配置条目并继承客户端并在控制器上注册。
ConcreteRepository
继承了 RepositoryBase
并且都继承了 SettingsClient
以便能够在控制器上注册(在 Java 中 ConcreteRepository
会 extend
RepositoryBase
并且两者都会 implement
SettingsClient
).
我的问题是:编译器警告我不要这样做,因为它不明确。遗憾的是,虚拟继承在这里对我没有帮助,因为它会覆盖 ConcreteRepository
或 RepositoryBase
的已实现功能,因此禁用两个 classes 之一的刷新功能。
有什么方法可以实现这种继承-观察者-结合?我认为这可能是一个设计缺陷,RepositoryBase
需要成为 ConcreteRepository
(?)
的成员
这里有一些代码可以给出一个概述:
#include <vector>
#include <iostream>
class SettingsClient {
public:
virtual void reloadSettings() = 0;
};
class SettingsController {
void notify(){
for(SettingsClient* client : clients){
client->reloadSettings(); // error! reloadSettings() of RepositoryBase or ConcreteRepository?
}
}
void registerClient(SettingsClient *client) {
clients.push_back(client);
}
std::vector<SettingsClient*> clients;
};
class RepositoryBase : private SettingsClient {
// ...
virtual void reloadSettings() {
std::cout << "Reloading Base!" << "\n";
}
// ...
};
class ConcreteRepository : private SettingsClient, private RepositoryBase {
// ...
virtual void reloadSettings() {
std::cout << "Reloading ConcreteRepository!" << "\n";
}
// ...
};
ConcreteRepository
没有必要也继承自 SettingsClient
。您可以在基础 class 构造函数中注册您的存储库一次,然后从子class 版本调用基础 class reloadSettings
。
class RepositoryBase : private SettingsClient
{
public:
RepositoryBase(SettingsController& controller)
{
controller.registerClient(this);
}
private:
void reloadSettings() override
{
std::cout << "Reloading RepositoryBase\n";
}
};
class ConcreteRepository : public RepositoryBase
{
public:
using RepositoryBase::RepositoryBase;
private:
void reloadSettings() override
{
std::cout << "Reloading ConcreteRepository\n";
}
};
我们使用 private 从 SettingsClient
继承来防止 reloadSettings
在 subclasses 中公开可见。如果我们需要调用基础class函数,我们可以使用protected继承来代替,代价是削弱了封装。
class RepositoryBase : protected SettingsClient
{
public:
RepositoryBase(SettingsController& controller)
{
controller.registerClient(this);
}
protected:
void reloadSettings() override
{
std::cout << "Reloading RepositoryBase\n";
}
};
class ConcreteRepository : public RepositoryBase
{
public:
using RepositoryBase::RepositoryBase;
protected:
void reloadSettings() override
{
RepositoryBase::reloadSettings();
std::cout << "Reloading ConcreteRepository\n";
}
};
如果您需要或更愿意为基本 class 和子 class 注册两个单独的客户端,您可以使用组合。
class RepositoryBase
{
public:
RepositoryBase(SettingsController& controller)
{
controller.registerClient(&client);
}
private:
struct : public SettingsClient
{
void reloadSettings() override
{
std::cout << "Reloading RepositoryBase\n";
}
} client;
};
class ConcreteRepository : public RepositoryBase
{
public:
ConcreteRepository(SettingsController& controller) : RepositoryBase(controller)
{
controller.registerClient(&client);
}
private:
struct : public SettingsClient
{
void reloadSettings() override
{
std::cout << "Reloading ConcreteRepository\n";
}
} client;
};
如果您需要访问其非静态成员,您可以为客户端提供指向其存储库的指针。
我有一个 class 管理配置文件 SettingsController
,它允许注册一个 SettingsClient
(一个纯虚函数,无成员)。 SettingsClient
将在相关配置条目更改时收到通知,以便刷新。
现在我有一个 RepositoryBase
,它需要一些配置条目,因此继承 SettingsClient
并在 SettingsController
和一个具体的存储库 ConcreteRepository
注册,它也需要一些配置条目并继承客户端并在控制器上注册。
ConcreteRepository
继承了 RepositoryBase
并且都继承了 SettingsClient
以便能够在控制器上注册(在 Java 中 ConcreteRepository
会 extend
RepositoryBase
并且两者都会 implement
SettingsClient
).
我的问题是:编译器警告我不要这样做,因为它不明确。遗憾的是,虚拟继承在这里对我没有帮助,因为它会覆盖 ConcreteRepository
或 RepositoryBase
的已实现功能,因此禁用两个 classes 之一的刷新功能。
有什么方法可以实现这种继承-观察者-结合?我认为这可能是一个设计缺陷,RepositoryBase
需要成为 ConcreteRepository
(?)
这里有一些代码可以给出一个概述:
#include <vector>
#include <iostream>
class SettingsClient {
public:
virtual void reloadSettings() = 0;
};
class SettingsController {
void notify(){
for(SettingsClient* client : clients){
client->reloadSettings(); // error! reloadSettings() of RepositoryBase or ConcreteRepository?
}
}
void registerClient(SettingsClient *client) {
clients.push_back(client);
}
std::vector<SettingsClient*> clients;
};
class RepositoryBase : private SettingsClient {
// ...
virtual void reloadSettings() {
std::cout << "Reloading Base!" << "\n";
}
// ...
};
class ConcreteRepository : private SettingsClient, private RepositoryBase {
// ...
virtual void reloadSettings() {
std::cout << "Reloading ConcreteRepository!" << "\n";
}
// ...
};
ConcreteRepository
没有必要也继承自 SettingsClient
。您可以在基础 class 构造函数中注册您的存储库一次,然后从子class 版本调用基础 class reloadSettings
。
class RepositoryBase : private SettingsClient
{
public:
RepositoryBase(SettingsController& controller)
{
controller.registerClient(this);
}
private:
void reloadSettings() override
{
std::cout << "Reloading RepositoryBase\n";
}
};
class ConcreteRepository : public RepositoryBase
{
public:
using RepositoryBase::RepositoryBase;
private:
void reloadSettings() override
{
std::cout << "Reloading ConcreteRepository\n";
}
};
我们使用 private 从 SettingsClient
继承来防止 reloadSettings
在 subclasses 中公开可见。如果我们需要调用基础class函数,我们可以使用protected继承来代替,代价是削弱了封装。
class RepositoryBase : protected SettingsClient
{
public:
RepositoryBase(SettingsController& controller)
{
controller.registerClient(this);
}
protected:
void reloadSettings() override
{
std::cout << "Reloading RepositoryBase\n";
}
};
class ConcreteRepository : public RepositoryBase
{
public:
using RepositoryBase::RepositoryBase;
protected:
void reloadSettings() override
{
RepositoryBase::reloadSettings();
std::cout << "Reloading ConcreteRepository\n";
}
};
如果您需要或更愿意为基本 class 和子 class 注册两个单独的客户端,您可以使用组合。
class RepositoryBase
{
public:
RepositoryBase(SettingsController& controller)
{
controller.registerClient(&client);
}
private:
struct : public SettingsClient
{
void reloadSettings() override
{
std::cout << "Reloading RepositoryBase\n";
}
} client;
};
class ConcreteRepository : public RepositoryBase
{
public:
ConcreteRepository(SettingsController& controller) : RepositoryBase(controller)
{
controller.registerClient(&client);
}
private:
struct : public SettingsClient
{
void reloadSettings() override
{
std::cout << "Reloading ConcreteRepository\n";
}
} client;
};
如果您需要访问其非静态成员,您可以为客户端提供指向其存储库的指针。