模式动机(Observer Pattern):将数据的存储与显示进行分离设计,能够很好地降低模块直接的耦合性。但是我们在后台更新数据时总希望前台的显示也做出相应的变化,观察者模式很好地解决了这个问题。
观察者模式定义了对象之间的一种一对多的依赖关系,当一个对象的状态发生变化时,所有依赖于它的对象都得到通知并自动更新状态,即对于一份后台数据,可以存在多个显示器负责显示并同时接收后台数据变更的通知。
观察者模式包含如下参与者:
Subject:
1> 可以有任意多个观察者同时观察同一目标
2> 可以添加观察者、删除观察者
Observer:
1> 为那些在目标发生改变时需要得到通知的对象定义一个更新接口
ConcreteSubject:
1> 将当前状态subjectState存到具体观察者ConcreteObserver中
2> 当状态发生改变时,及时通知各个观察者更新状态
ConcreteObserver:
1> 维护一个指向ConcreteSubject对象的指针subject
2> 存储有关状态observerState,该状态应该与具体目标的状态subjectState保持一致
当ConcreteSubject中的状态发生改变时,其会通知ConcreteObserver更新状态以保持状态的一致性。一个目标可以存在任意个观察者对象,一旦目标的状态发生改变,所有的观察者都会接到通知,但是目标对象可以不关心到底谁是它的观察者,其只需要调用Notify()就可以了。
模式结构图:
模式代码:
bt_观察者模式.h:
1 #ifndef OP_H 2 #define OP_H 3 #include <iostream> 4 #include <list> 5 using namespace std; 6 7 /* 8 抽象观察者: 9 1> 为那些在目标发生改变时需要得到通知的对象定义一个更新接口 10 */ 11 class Subject; 12 class Observer 13 { 14 public: 15 virtual ~Observer(){ } 16 virtual void Update() = 0; 17 }; 18 19 /* 20 抽象目标: 21 1> 可以有任意多个观察者同时观察同一目标 22 2> 可以添加观察者、删除观察者 23 */ 24 class Subject 25 { 26 public: 27 Subject(){ observers = new list<Observer*>; } 28 virtual ~Subject(){ delete observers; } 29 void Attach(Observer* obv) 30 { 31 cout << "添加具体观察者" << endl; 32 observers->insert(observers->end(), obv); 33 } 34 void Detach(Observer* obv) 35 { 36 cout << "删除具体观察者" << endl; 37 delete obv; 38 observers->remove(obv); 39 } 40 void Notify() 41 { 42 cout << "通知具体观察者更新状态->" << endl; 43 list<Observer*>::iterator iter; 44 for(iter = observers->begin(); iter != observers->end(); iter++) 45 { 46 (*iter)->Update(); 47 } 48 } 49 50 private: 51 list<Observer*>* observers; 52 }; 53 54 /* 55 具体目标: 56 1> 将当前状态subjectState存到具体观察者ConcreteObserver中 57 2> 当状态发生改变时,及时通知各个观察者更新状态 58 */ 59 class ConcreteSubject : public Subject 60 { 61 public: 62 int GetState(){ return subjectState; } 63 void SetState(int s) 64 { 65 subjectState = s; 66 Notify(); // 通知所有观察者更新状态 67 } 68 69 private: 70 int subjectState; 71 }; 72 /* 73 具体观察者: 74 1> 维护一个指向ConcreteSubject对象的指针subject 75 2> 存储有关状态observerState,该状态应该与具体目标的状态subjectState保持一致 76 */ 77 class ConcreteObserver : public Observer 78 { 79 public: 80 ConcreteObserver(ConcreteSubject* cs, int state) : subject(cs), observerState(state){} 81 virtual void Update() 82 { 83 cout << "->具体观察者更新状态" << endl; 84 cout << "更新前状态为:" << observerState << endl; 85 observerState = subject->GetState(); 86 cout << "更新后状态为:" << observerState << endl; 87 } 88 89 private: 90 ConcreteSubject* subject; 91 int observerState; 92 }; 93 94 #endif // OP_H
测试用例.cpp:
1 #include "bt_观察者模式.h" 2 3 int main() 4 { 5 6 cout << "***** 观察者模式测试 *****" << endl; 7 ConcreteSubject* cs = new ConcreteSubject; 8 Observer* observer1 = new ConcreteObserver(cs, 6666); 9 Observer* observer2 = new ConcreteObserver(cs, 7777); 10 cs->Attach(observer1); // 注册观察者 11 cs->Attach(observer2); // 注册观察者 12 cs->SetState(8888); 13 14 // cs->Notify(); // 由客户调用Notify 15 16 delete observer2; 17 delete observer1; 18 delete cs; 19 20 return 0; 21 }
![](https://images0.cnblogs.com/blog2015/759985/201506/141623359883293.png)
模式分析:
:: 观察者模式将众多观察者和其依赖的具体目标进行解耦,使得每个目标仅知道存在观察者,但是不知道具体观察者是什么类型的。
:: 目标发送通知时使用广播方式,凡是被登记过的观察者都会接收到并及时更新状态。
:: Notify()函数由谁来调用呢?
如果是由具体目标对象调用Notify(),那么必须每改变一次状态,就调用一次Notify()。但是,如果让客户负责调用Notify(),虽然可以对于多次改变只调用一次Notify(),但是可能容易忘记调用,导致丢失状态。上例中就是由具体目标负责调用Notify的例子,如下所示为由客户调用Notify()的例子:
在ConcreteSubject::SetState(int s)中去掉Notify():
void SetState(int s)
{
subjectState = s;
}
主程序中增加cs->Notify()语句即可,结果和上述完全相同。
:: 当然也可以扩展目标的注册接口,使得不同类型的观察者只对特定类型的事件感兴趣,这样可以提高观察者更新状态的效率。当一种事件发生,目标仅通知那些注册为对该类事件感兴趣的观察者。
:: 如果依赖关系比较复杂,比如一个观察者依赖多个具体目标,而每个具体目标又对应多个观察者,这种多对多的依赖关系可以由一个专门的中介者来处理。一般而言,这种类型的中介者只应该存在唯一实例,因此最好用单例来设计它。