一、观察者模式的定义
观察者模式是一种行为设计模式,它定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象,这个主题对象在状态发生变化时,会通知所有依赖于它的观察者对象,使它们能够自动更新自己的行为;这些观察者之间也没有相互联系,它们仅根据需求自己去attach主题对象,而主题对象并不知道也不关心观察者有哪些,做到了在两个模块间划定清晰的界线。
二、观察者模式的角色
观察者模式必须包含两个角色:观察者和被观察对象。然而,观察者和被观察对象之间的互动关系不能体现成类之间的直接调用,否则就将使观察者和被观察对象之间紧密的耦合起来,从根本上违反面向对象的设计原则。根据面向接口编程的原则,则自然就有抽象主题角色和抽象观察者角色。
- Subject 目标: 它把所有观察者对象的引用保存到一个聚集里,每个主题都可以有任何数量的观察者。抽象主题提供一个接口,可以增加和删除观察者对象。
- ConcreteSubject 具体目标: 将有关状态存入具体观察者对象;在具体目标内部状态改变时,给所有登记过的观察者发出通知。
- Observer 观察者: 为所有的具体观察者定义一个接口,在得到主题通知时更新自己。
- ConcreteObserver 具体观察者: 实现观察者角色所要求的更新接口,以便使本身的状态与具体目标状态协调。
三、观察者模式的结构
观察者模式包含四个角色:目标又称为主题,它是指被观察的对象;具体目标是目标类的子类,通常它包含有经常发生改变的数据,当它的状态发生改变时,向它的各个观察者发出通知;观察者将对观察目标的改变做出反应;在具体观察者中维护一个指向具体目标对象的引用,它存储具体观察者的有关状态,这些状态需要和具体目标的状态保持一致。
1.观察者模式的类图
该图源来自网络
观察者将自己注册到被观察者的容器中时,被观察者不应该过问观察者的具体类型,而是应该使用观察者的接口,将观察者存放在自己的一个容器里;观察者告诉被观察者要撤销观察,被观察者就从其容器中将观察者去除;被观察对象发生了某种变化,就从其容器中得到所有注册过的观察者,将变化通知观察者。
2.观察者模式的时序图
该图源来自网络
四、观察者模式的应用场合
1.当对一个对象的改变需要同时改变其他对象,而又不知道具体有多少对象有待改变的情况下。
2.当一个对象必须通知其他对象,而又不能假定其他对象是谁的情况下。
3.需要在系统中创建一个触发链,A对象的行为将影响B对象,B对象的行为将影响C对象……,可以使用观察者模式创建一种链式触发机制。
五、观察者模式的优缺点
1.优点
- 观察者模式在被观察者和观察者之间建立了一个抽象的耦合,被观察者并不知道任何一个具体的观察者,只是保存着抽象观察者的列表,每个具体观察者都符合一个抽象观察者的接口。
- 支持广播通信
- 观察者模式符合“开闭原则”的要求
2.缺点
- 如果一个被观察者有很多直接和间接的观察者时,将所有的观察者都通知到会花费很多时间。
- 观察者模式没有相应的机制使观察者知道所观察的对象是怎样发生变化的。
- 如果在被观察者之间有循环依赖的话,被观察者会触发它们之间进行循环调用,导致系统崩溃。
六、观察者模式与发布-订阅模式、监听事件的区别
1.观察者模式与发布订阅模式(转载自https://www.cnblogs.com/lovesong/p/5272752.html)
在观察者模式中,具体观察者把自己注册到具体目标里,具体目标发生变化时,由具体目标调度观察者的更新方法。
在发布订阅模式中,订阅者把自己想订阅的事件注册到调度中心,当该事件触发时候,发布者发布该事件到调度中心(顺带上下文),由调度中心统一调度订阅者注册到调度中心的处理代码。
2.观察者模式与监听事件
一种是设计模式,一种是回调机制。
七、观察者模式的实例
披萨快递系统实例地址:https://github.com/DuyguKeskek/Pizza-Delivery-System
目标类代码 https://github.com/DuyguKeskek/Pizza-Delivery-System/blob/master/Pizza-Delivery-System.cpp#L17
1 // Abstract subject class 2 class Subject { 3 public: 4 virtual void Attach(Observer *observer) = 0; 5 6 virtual void Detach(Observer *observer) = 0; 7 8 virtual void Notify() = 0; 9 10 protected: 11 vector<Observer *> _observers; 12 };
观察者类代码 https://github.com/DuyguKeskek/Pizza-Delivery-System/blob/master/Pizza-Delivery-System.cpp#L30
1 // Abstract Observer class 2 class Observer { 3 public: 4 virtual void Update(PizzaCompanyFactory *companyFactory) = 0; 5 };
具体目标(披萨公司)类 https://github.com/DuyguKeskek/Pizza-Delivery-System/blob/master/Pizza-Delivery-System.cpp#L36
1 // Abstract Factory and Concrete Subject 2 class PizzaCompanyFactory : public Subject { 3 public: 4 virtual Pizza *CreatePizza() = 0; 5 6 virtual Drink *CreateDrink() = 0; 7 8 virtual string GetCompanyName() = 0; 9 10 virtual int GetDistance() = 0; 11 12 void Action(int type) { 13 if (type == 0) { 14 CreatePizza(); 15 } else { 16 CreateDrink(); 17 } 18 } 19 20 void Attach(Observer *observer) { 21 _observers.push_back(observer); 22 } 23 24 void Detach(Observer *observer) { 25 int count = _observers.size(); 26 int i; 27 28 for (i = 0; i < count; i++) { 29 if (_observers[i] == observer) 30 break; 31 } 32 if (i < count) 33 _observers.erase(_observers.begin() + i); 34 } 35 36 void Notify() { 37 // set argument to something that helps 38 // tell the Observers what happened 39 int count = _observers.size(); 40 for (int i = 0; i < count; i++) { 41 (_observers[i])->Update(this); 42 } 43 } 44 45 void SendReport() { 46 // Whenever a change happens to _price, notify 47 // observers. 48 Notify(); 49 } 50 };
具体观察者(工作分配中心)类 https://github.com/DuyguKeskek/Pizza-Delivery-System/blob/master/Pizza-Delivery-System.cpp#L243
1 class DistributorCenter : public Observer { 2 private: 3 string _investor_name; 4 5 DistributorCenter() { } 6 7 DistributorCenter(string name) : _investor_name(name) { }; 8 9 static DistributorCenter *instance; 10 public: 11 12 static DistributorCenter *getInstance(string name) { 13 if (instance == NULL) { 14 instance = new DistributorCenter(name); 15 } 16 return instance; 17 } 18 19 void Update(PizzaCompanyFactory *companyFactory) { 20 cout << "\nNotified" << "\n"; 21 Command *command = new PizzaRequestCommand(companyFactory); 22 23 TruckDriver *driver; 24 25 int number = rand() % 100 + 1; // generate random between 1-100 26 if (number < 50) { 27 driver = new DayTruckDriver(); 28 } else { 29 driver = new NightTruckDriver(); 30 } 31 32 // Small builder 33 driver->TakeOrder(command, companyFactory); 34 driver->ManageOrder(); 35 } 36 };
参考文章:https://design-patterns.readthedocs.io/zh_CN/latest/behavioral_patterns/observer.html