通用观察者模式
generic observer pattern
我正在使用代码,其中有很多观察者模式实现。所有这些都是这样组织的:
观察者要实现的一些接口:
class ObserverInterface {
virtual void FooOccurs() = 0;
};
一些 class,实现了注册、注销和通知:
class ObservableImpl {
public:
Register(ObserverInterface *observer);
Unregister(ObserverInterface *observer);
private:
void SomeMethod() {
// foo things
for(auto &observer: observers) {
observer.FooOccurs();
}
}
};
每次都是Register和Unregister的copy-paste以及ObserverInterface各个方法的notification实现。并且每次程序员都必须记住调用 Unregister() 时,如果它的观察者将被销毁。
我希望将观察者模式包含在两个 class 模板中。到目前为止,我有类似的东西:
http://rextester.com/UZGG86035
但我不确定我是不是在重新发明轮子。有没有更简单、众所周知的方法来做到这一点?
在 C++11 中,我建议使用基于标记的方法。
您注册一个观察员。观察者只是 std::function<void(Signature...)>
.
注册函数return一个token,一个std::shared_ptr<void>
。只要 returned shared_ptr
有效,广播公司就会继续向该听众广播。
监听器现在负责维护 std::shared_ptr
生命周期。
在广播员内部,您在广播前持有 weak_ptr
和 .lock()
它。如果我真的不需要注销(通常我不需要),我会懒洋洋地清理我的 weak_ptr
列表。否则,我shared_ptr
我return有删除功能,可以注销。
或者,您的听众是 shared_ptr<std::function<void(Args...)>>
,并且在内部存储 weak_ptr
相同。
在此模型中,您无法轻易注入注销函数。但是,这确实意味着他们可以自己使用别名构造函数将回调的生命周期紧紧绑定到自己身上,假设他们由 shared_ptr.
管理。
根据我的经验,让听众保持 std::vector<token>
就足够了。如果他们有更复杂的聆听关系,他们可以做更多的工作,维护密钥等。
混合模型也是可能的。
这两个对于非线程安全的广播来说都是可以接受的,并且可以用几十行代码编写。
线程安全广播变得棘手。通常我发现你最好为此使用消息传递模式而不是其他选择,因为这会稍微降低推理并发性的难度。
这也不处理您想随意注册听众的情况,广播者和听众的生命周期就像爆米花一样。
我正在使用代码,其中有很多观察者模式实现。所有这些都是这样组织的:
观察者要实现的一些接口:
class ObserverInterface {
virtual void FooOccurs() = 0;
};
一些 class,实现了注册、注销和通知:
class ObservableImpl {
public:
Register(ObserverInterface *observer);
Unregister(ObserverInterface *observer);
private:
void SomeMethod() {
// foo things
for(auto &observer: observers) {
observer.FooOccurs();
}
}
};
每次都是Register和Unregister的copy-paste以及ObserverInterface各个方法的notification实现。并且每次程序员都必须记住调用 Unregister() 时,如果它的观察者将被销毁。
我希望将观察者模式包含在两个 class 模板中。到目前为止,我有类似的东西: http://rextester.com/UZGG86035
但我不确定我是不是在重新发明轮子。有没有更简单、众所周知的方法来做到这一点?
在 C++11 中,我建议使用基于标记的方法。
您注册一个观察员。观察者只是 std::function<void(Signature...)>
.
注册函数return一个token,一个std::shared_ptr<void>
。只要 returned shared_ptr
有效,广播公司就会继续向该听众广播。
监听器现在负责维护 std::shared_ptr
生命周期。
在广播员内部,您在广播前持有 weak_ptr
和 .lock()
它。如果我真的不需要注销(通常我不需要),我会懒洋洋地清理我的 weak_ptr
列表。否则,我shared_ptr
我return有删除功能,可以注销。
或者,您的听众是 shared_ptr<std::function<void(Args...)>>
,并且在内部存储 weak_ptr
相同。
在此模型中,您无法轻易注入注销函数。但是,这确实意味着他们可以自己使用别名构造函数将回调的生命周期紧紧绑定到自己身上,假设他们由 shared_ptr.
管理。根据我的经验,让听众保持 std::vector<token>
就足够了。如果他们有更复杂的聆听关系,他们可以做更多的工作,维护密钥等。
混合模型也是可能的。
这两个对于非线程安全的广播来说都是可以接受的,并且可以用几十行代码编写。
线程安全广播变得棘手。通常我发现你最好为此使用消息传递模式而不是其他选择,因为这会稍微降低推理并发性的难度。
这也不处理您想随意注册听众的情况,广播者和听众的生命周期就像爆米花一样。