如何结合继承实现观察者模式?

How do I realize the Observer pattern in conjunction with inheritance?

我有一个 class 管理配置文件 SettingsController,它允许注册一个 SettingsClient(一个纯虚函数,无成员)。 SettingsClient 将在相关配置条目更改时收到通知,以便刷新。

现在我有一个 RepositoryBase,它需要一些配置条目,因此继承 SettingsClient 并在 SettingsController 和一个具体的存储库 ConcreteRepository 注册,它也需要一些配置条目并继承客户端并在控制器上注册。

ConcreteRepository 继承了 RepositoryBase 并且都继承了 SettingsClient 以便能够在控制器上注册(在 Java 中 ConcreteRepositoryextend RepositoryBase 并且两者都会 implement SettingsClient).

我的问题是:编译器警告我不要这样做,因为它不明确。遗憾的是,虚拟继承在这里对我没有帮助,因为它会覆盖 ConcreteRepositoryRepositoryBase 的已实现功能,因此禁用两个 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";
  }
};

我们使用 privateSettingsClient 继承来防止 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;
};

如果您需要访问其非静态成员,您可以为客户端提供指向其存储库的指针。