本篇介绍了观察者模式及相关的面向对象设计原则。观察者模式定义了对象之间的一对多依赖,当一个对象的状态改变时,他的所有依赖者都会收到通知并自动更新。
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 ; }