这个设计模式的功能就是实现订阅与发布的功能,一旦有消息更新,即可将最新的消息同步给所有的订阅者,无需订阅者主动来询问,如果订阅者无需这些消息了,只需要取消订阅即可,后续有新的消息就不会再次打扰。
适用于对于相同的数据需要表现为不同的行为的功能,比如,得知爸爸要回来了,儿子就会立马停止玩游戏,妈妈立马开始准备晚餐。也有可能是很多人都想订阅这些数据,比如求职者先将要找工作的需求交给猎头,猎头那里有很多这样的需求,一旦有公司有需求,猎头那里就会立马通知所有的要找这份工作的求职者,让他们分别去面试,公司选择优秀的人才入职。
生活中有很多类似的场景,都可以用观察者模式来解释,所以设计模式的灵感也来源于生活。另外一方面,也实现了逻辑与应用的分离,从上面的例子也可以看出,底层数据只有一份,但是可以用在几个不同的场景,如果新的场景,也就相当于多了一个订阅者而已。
基于模板实现的观察者模式
网上面有很多实现了的观察者模式,用的都是普通类,但由于模型应用场景,比如订阅的消息类型各式各样,有string、int或者自定义class,那就需要改用模板类了。
由普通类改为模板类确实费了一点功夫,主要是对于模板类的不够熟悉,容易踩很多坑,比如,继承模板类的子类中,如果父类的成员变量是protect的,那子类是无法直接访问的,需要加this指针或者父类的作用域限定符。如果在成员函数里面使用this指针进行共享,注意不能直接使用std::shared_ptr1
2
3
4
5
6class MyClass : public std::enable_shared_from_this<MyClass> {
public:
std::shared_ptr<MyClass> getSharedPtr() {
return shared_from_this();
}
};
还有,对于模板类的实现需要和声明放在同一个文件中,否则链接的时候就会找不到定义,这一点和声明普通类不一样的,需要特别注意。
另外,订阅者类中在调用观察者对象时,应该用观察者的抽象类,同时,观察者类在调用订阅者类时,也应该使用订阅者的抽象类,否则会相会包含,编译就会报错,而把调用类抽象出来就避免了这个问题,再实际实例化对象时再new具体对象就行,可以看作时多态的一种体现,也可以说是面向接口编程,不要面向具体类编程。语言描述不太清楚,类图参考入下,使用draw.io绘制。
可以看出,具体类之间是没有相互关联的,有什么事请找抽象类,这样就一目了然了,后面再扩展别的观察者类的时候,只需要按照抽象类的接口实现的,就都可以进行订阅消息,不需要更改原来的类,所以是对修改关闭,对扩展开放,符合设计模式基本原则。
代码实现
参考了别人的代码,实现了两个版本,上面的uml图为第二个版本。
1 |
|
第二个版本是将类分文件整理了,另外在观察者中可以进行订阅和取消订阅,比如观察者update出问题的时候,可以选择自动取消订阅。
1 | // IObservable.hpp |