本篇介绍了观察者模式及相关的面向对象设计原则。观察者模式定义了对象之间的一对多依赖,当一个对象的状态改变时,他的所有依赖者都会收到通知并自动更新。
1 引例 有一个气象站系统需要构建,系统中包含三部分:
其中气象站负责感应气象数据,WeatherData 对象从气象站获取数据并更新气象显示装置,气象显示装置有不同的显示界面,比如目前状况、气象统计、天气预报等等,未来还可能加入新的显示界面。
一种实现方式是:
1 2 3 4 5 6 7 8 9 10 11 12 13 class WeatherData { void measurementsChanged () { float temp = getTemperature (); float humidity = getHumidity (); float pressure = getPressure (); currentConditionsDisplay.update (temp, humidity, pressure); statisticsDisplay.update (temp, humidity, pressure); forecastDisplay.update (temp, humidity, pressure); } };
显然这样的实现方式违背了上一节提到的诸多设计原则,比如没有把不变和变化的代码分开,如果后续需要加入新的显示面板,就要更改 WeatherData 的代码,再比如调用面板的update()
方法没有面向接口编程,而是一个一个的调用了具体面板的接口。因此这样的设计会为后续的系统维护带来巨大的成本。
2 观察者模式 观察者模式定义了对象之间的一对多依赖,当一个对象的状态改变时,他的所有依赖者都会收到通知并自动更新。
观察者模式通常通过构建 Subject 接口 Observer 接口来实现:
观察者模式提供了一种对象设计,让主题对象和观察者对象之间松耦合 。所谓松耦合是指两个对象之间依然可以交互,但彼此不清楚对方的细节。松耦合的好处在于,主题对象只知道观察者实现了 Observer 接口,而无需关注观察者具体是谁,做了什么或者任何其他细节。在代码运行的任何时候都可以增加或者删除观察者,主题不会受到任何影响,它只在状态改变时,遍历观察者列表并通知他们。当我们改变主题或者观察者任意一方时,另一方都不会受到任何影响,只要它们之间的接口依然被遵守。因此使用松耦合设计系统会更有的弹性,更容易应对变化,因为对象之间的互相依赖降到了最低。
设计原则:为了交互对象之间的松耦合而努力。
回顾上面的气象站项目,显然 WeatherData 对象就是主题,主题在实现的时候通常也被称为 Observable(可观察对象),也就是被观察者,而不同的显示面板就是观察者。
于是我们首先需要实现两个接口:
1 2 3 4 5 6 7 8 9 10 11 12 class Observer {public : virtual void update (float temp, float humidity, float pressure) = 0 ; }; class Observable {public : virtual void registerObserver (Observer* o) = 0 ; virtual void removeObserver (Observer* o) = 0 ; virtual void notifyObservers () = 0 ; };
然后实现 WeatherData 类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 class WeatherData : public Observable {private : std::list<Observer*> observers; float temperature; float humidity; float pressure; public : virtual void registerObserver (Observer* o) { observers.push_back (o); } virtual void removeObserver (Observer* o) { observers.remove (o); } virtual void notifyObservers () { std::list<Observer*>::iterator it = observers.begin (); while (it != observers.end ()) { (*it)->update (temperature, humidity, pressure); ++it; } } void measurementsChanged () { notifyObservers (); } };
某个观察者显示面板的实现更为简单:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 class CurrentConditionsDisplay : public Observer {private : float temperature; float humidity; Observable* weatherData; public : CurrentConditionsDisplay (Observable* o) : weatherData (o) {} virtual void update (float temp, float humidity, float pressure) { this ->temperature = temp; this ->humidity = humidity; display (); } void display () { } };
这样一个简单的观察者模式就是实现完毕了,这是经典的观察者模式实现,但存在一些缺陷:
需要继承,继承是强对象关系,只能对特定的观察者才有效,即必须是Observer抽象类的派生类才行
观察者被通知的接口参数不支持变化,导致观察者不能应付接口的变化,并且这个观察者还不能带参数
3 改进的观察者模式 C++11 提供的仿函数和模板等特性可以解决上述问题。C++11 实现的观察者模式,内部维护了一个泛型函数列表,观察者只需要将观察者函数注册进来即可,消除了继承导致的强耦合。通知接口还使用了可变参数模板,支持任意数量的参数,消除了接口变化的影响。
改进之后的观察者模式和 C# 中的 event 类似,通过定义观察者的模板类型来限定观察者,即不要求观察者必须为某个派生类,只要求所有观察者使用相同的函数模板即可,当需要和原来不同的观察者时,只需要定义一个新的 event 类型即可。
需要注意的是 event 对象不能拷贝和复制,这可以通过 delete 关键字来实现。
一个改进的观察者模式实现如下:
observer.hpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 #ifndef _OBSERVER_HPP_ #define _OBSERVER_HPP_ #include <iostream> #include <string> #include <functional> #include <map> using std::cout;using std::endl;using std::string;using std::map; class NonCopyable {public : NonCopyable (const NonCopyable& n) = delete ; NonCopyable& operator =(const NonCopyable& n) = delete ; NonCopyable () = default ; }; template <typename Func>class Events : public NonCopyable {private : int m_observerId = 0 ; map<int , Func> m_connections; template <typename F> int Assign (F&& f) { int k = m_observerId++; m_connections.emplace (k, std::forward<F>(f)); return k; } public : int Connect (Func&& f) { return Assign (f); } int Connect (const Func& f) { return Assign (f); } void Disconnect (int key) { m_connections.erase (key); } template <typename ... Args> void Notify (Args&& ... args) { for (auto &it : m_connections) { auto & func = it.second; func (std::forward<Args>(args)...); } } }; #endif
main.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 #include "observer.hpp" using ObserverFunc = std::function<int (int , int )>; class Observer {public : int operator () (int a, int b) { cout << "Observer函数对象的事件2被调用" << endl; int res = a + b; return res; } }; class Observer2 {public : int observerFunc (int a, int b) { cout << "Observer2成员函数事件3被调用" << endl; int res = a + b; return res; } }; int gobserverFunc (int a, int b) { cout << "全局的gobserverFunc事件4被调用" << endl; int res = a + b; return res; } int main (void ) { Events<ObserverFunc> e; int lambdaID = e.Connect ([](int a, int b){ int res = a+b; cout << "lambda函数的事件1被调用" << endl; return res; }); int obsID = e.Connect (Observer ()); Observer2 o2; int obsID1 = e.Connect (std::bind (&Observer2::observerFunc, o2, std::placeholders::_1, std::placeholders::_2)); int gID4 = e.Connect (gobserverFunc); e.Notify (1 , 2 ); return 0 ; }