通用观察者模式

generic observer pattern

本文关键字:观察者模式      更新时间:2023-10-16

我正在使用代码,它有很多观察者模式实现。所有这些都以这样的方式组织:

观察者要实现的一些接口:

class ObserverInterface {
  virtual void FooOccurs() = 0;
};

一些类,它实现了注册、注销和通知:

class ObservableImpl {
  public:
    Register(ObserverInterface *observer);
    Unregister(ObserverInterface *observer);
  private:
    void SomeMethod() {
      // foo things
      for(auto &observer: observers) {
        observer.FooOccurs();
      }
    }
};

每次复制粘贴注册和取消注册以及实现观察者接口的每个方法的通知时。每次程序员必须记住调用 Unregister(( 时,如果它的观察者将被破坏。

我希望将观察者模式包含在两个类模板中。到目前为止,我有这样的东西:http://rextester.com/UZGG86035

但我不确定我是否没有重新发明轮子。有没有更简单的、众所周知的方法可以做到这一点?

在 C++11 中,我建议采用基于令牌的方法。

注册观察者。 观察者只是一个std::function<void(Signature...)>.

注册函数返回一个令牌,一个std::shared_ptr<void>。 只要返回的shared_ptr有效,广播公司将继续向该听众广播。

侦听器现在负责维护该std::shared_ptr生存期。

在广播公司内部,您拿着一个weak_ptr并在广播前.lock()它。 如果我真的不需要取消注册(通常我不需要(,我会懒洋洋地清理我的weak_ptr列表。 否则,我返回shared_ptr具有执行注销的删除功能。


或者,您的听众是shared_ptr<std::function<void(Args...)>>的,并且在内部存储weak_ptr

在此模型中,您无法轻松注入取消注册函数。 但是,这确实意味着他们可以使用别名构造函数本身将回调的生存期与自身紧密绑定,假设它们由shared_ptr管理。

根据我的经验,只需让听众保持std::vector<token>就足够了。 如果他们有更复杂的倾听关系,他们可以做更多的工作,维护密钥等。

混合模型也是可能的。


对于非线程安全的广播,这两者都是可以接受的,并且可以用几十行代码编写。

线程安全广播变得棘手。 通常,我发现您最好为此使用消息传递模式而不是替代方案,因为这会稍微降低推理并发的难度。

这也不能处理您想要随意注册听众的情况,广播公司和听众的生命周期就像爆米花一样。