如何实现与继承一起的Observer模式?
我有一个管理配置文件SettingsController
的类,它允许注册一个SettingsClient
(一个纯虚函数,没有成员)。当相关配置条目发生更改时,将会通知SettingsClient
,以便刷新。如何实现与继承一起的Observer模式?
现在我有一个RepositoryBase
其需要一些配置条目并因此继承SettingsClient
并在SettingsController
和混凝土库ConcreteRepository
这也需要一些配置条目并且继承客户端和寄存器在控制器注册。
ConcreteRepository
继承RepositoryBase
无一不继承SettingsClient
能够在控制器注册(在Java中ConcreteRepository
将extend
的RepositoryBase
两者将implement
的SettingsClient
)。
我的问题是:编译器警告我不要这样做,因为它是模棱两可的。可悲的虚拟继承在这里不会帮助我,因为它会覆盖ConcreteRepository
或RepositoryBase
的已实现函数,因此禁用两个类之一的刷新功能。
有没有办法实现这种继承 - 观察者组合?我想这可能是一个设计上的缺陷和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
。您可以在基类构造函数中注册一次仓库,然后从子类版本调用基类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";
}
};
我们使用私人继承从SettingsClient
防止reloadSettings
被在子类中公开可见。如果我们需要调用基类函数,我们可以使用保护继承来代替,这是以弱化封装为代价的。
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 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;
};
如果您需要访问其非静态成员,则可以为客户端指定一个指向其存储库的指针。
公共继承因此多态性与工作,SettingsClient需要一个虚拟析构函数,ConcreteRepository不需要也继承SettingsClient,因为它已经是一个通过RepositoryBase,并显式调用RepositoryBase的ReloadSettings从ConcreteBase的,如果你需要。 C++中没有默认调用多个基类重写成员的机制。 – evan