首页 > 解决方案 > 超越基本思想理解 C++ 中的观察者模式

问题描述

我正在学习Head First Design Patterns中的设计模式,为了获得信心,我计划在学习完相应的章节后用 C++ 实现每个模式。

关于观察者模式,我真的在努力超越语言独立的主要思想。

我一直在浏览以下内容:

但是,一旦我开始用 C++ 编码,一些特定于语言的困难就暴露了我对整个主题的一些误解,我无法用上面的点赞来解决。但是,我在这里发帖,因为我有一个(看似)工作代码。

示例代码如下,之后我列出了我对这个模式的理解和使用的一些担忧。

#include <algorithm>
#include <iostream>
#include <unordered_map>
#include <unordered_set>

class Observer {
 public:
  virtual void update() = 0;
};

class Observable {
 protected:
  std::unordered_set<Observer*> observers;
 public:
  virtual void addObserver(Observer&) = 0;
  virtual void removeObserver(Observer&) = 0;
  virtual void notifyObservers() = 0;
};

class Virus : public Observable {
 public:
  void addObserver(Observer& o) {
    observers.insert(&o);
  };
  void removeObserver(Observer& o) {
    std::erase_if(observers, [&o](auto const& io){ return &o == io; });
  };
  void notifyObservers() {
    for (auto& o : observers) o->update();
  };
  void operator++() {
    ++spread;
    std::cout << "\nLevel: " << spread
              << "\nSending notifications:\n";
    notifyObservers();
  }
  int getSpread() { return spread; };
 private:
   int spread = 0;
};

class NormalCountry final : public Observer {
 private:
  void update() override {
    if (obs.getSpread() < 2)
      std::cout << "NormalCountry: What!? Coronavirus?\n";
    else
      std::cout << "NormalCountry: Ok, let's quarantine...\n";
  };
 private:
  Virus& obs;
 public:
  void selfUnsubscribe() {
    obs.removeObserver(*this);
  };
  void selfSubscribe() {
    obs.addObserver(*this);
  };
  NormalCountry() = delete;
  NormalCountry(Virus& o) : obs(o) {
    selfSubscribe();
  }
};

class BraveCountry final : public Observer {
 public:
  void update() override {
    std::cout << "BraveCountry: No worries, people, our antibodies are cooler!\n";
  };
};

int main() {
  Virus cv;
  NormalCountry it(cv);
  BraveCountry uk;
  ++cv;
  cv.addObserver(uk);
  ++cv;
  it.selfUnsubscribe();
  ++cv;
}

对模式本身的怀疑:

C++ 特有的疑问:

标签: c++observer-pattern

解决方案


  1. 关于观察者界面

Obesever 应该有更新(或 onChange 或类似的东西)方法。Obesever 几乎总是需要一些有关更改的上下文来更新自身。因此这里出现了推与拉的问题。在 push 中,observable 必须将上下文信息负载传递给观察者。但是现在问题来了,什么应该是满足所有观察者的有效载荷结构?当添加新的观察者需要一些不属于当前有效载荷结构的上下文参数时,有效载荷结构将如何演变?另一方面,在拉式设计中,观察者需要向可观察者查询新状态。这意味着 observable 需要向观察者公开适当的接口。推与拉之间的选择取决于用例。Push 提供了更好的解耦,但更新上下文信息应该足够简单。在拉式方法中,观察者可以查询 observable 并获得更复杂和自定义的状态信息。即使不同的观察者也可以以不同的方式查询 observable。但是 pull 方法在观察者和可观察者之间有更紧密的耦合。

  1. 关于 observable 中观察者的集合

观察者集合的三个主要用例 addObserver、removeObserver 和 notifyObservers。所以任何无序的集合都应该没问题。

  1. 关于观察员更新公开

update 是一种通知方式,因此必须在公共界面中可用。

C++ 特定

  1. Observable 应该保持弱引用(不延长观察者生命周期)观察者。
  2. 在观察者中保持对 observable 的引用:这不是一个好主意。它可以看作是 pull 方法的隐式实现。但是保留界面会比保留具体对象更好。

推荐阅读