【问题标题】:How do I realize the Observer pattern in conjunction with inheritance?如何结合继承实现观察者模式?
【发布时间】:2017-03-15 22:53:30
【问题描述】:

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

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

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

我的问题是:编译器警告我不要这样做,因为它是模棱两可的。遗憾的是,虚拟继承在这里对我没有帮助,因为它会覆盖 ConcreteRepositoryRepositoryBase 的已实现功能,因此禁用这两个类之一的刷新功能。

有没有办法实现这种继承-观察者-组合?我认为这可能是一个设计缺陷,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";
  }
  // ...
};

【问题讨论】:

  • 公有继承,所以多态性起作用,SettingsClient 需要一个虚拟析构函数,ConcreteRepository 不需要也继承 SettingsClient,因为它已经是通过 RepositoryBase 的一个,如果需要,可以从 ConcreteBase 显式调用 RepositoryBase 的 reloadSettings。 C++ 中没有默认调用多个基类覆盖成员的机制。

标签: c++ inheritance observer-pattern


【解决方案1】:

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 在子类中公开可见。如果我们需要调用基类函数,我们可以使用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 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;
};

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

【讨论】:

    猜你喜欢
    • 2011-12-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-02-12
    • 2013-10-28
    • 1970-01-01
    • 1970-01-01
    • 2011-06-23
    相关资源
    最近更新 更多