观察者模式又称发布–订阅模式,是属于对象行为性的一种设计模式。
首先是观察者模式的定义:
观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个对象,当这个主题对象发生改变的时候,会通知所有的观察者对象,使他们能自动更新自己。
此模式的结构图:
适用性:
1 当一个抽象模型有两个方面,其中一个方面依赖于另外一个方面,将这两个对象封装在独立的对象中以使他们独立的发生改变和复用
2 当一个对象的改变需要通知多个对象发生变化,而又不知道有多少个对象的时候
3 当一个对象必须通知其他对象,而又不能假定其他对象是谁的时候。
协作
1 当concreteSubject发生任何可能导致其观察者对象与其本身的状态不一致的改变时,它将通知它的各个观察者
2 在得到一个具体的改变通知后,ConcreteObserver对象可向目标对象查询信息,ConcreteObserver使用这些信息以使他的状态与目标对象的状态一致
优点
1 目标和观察者的解耦,在分层的结构当中,由于底层的对象可能要通知高层的对象发生改变,如果目标和观察者混在一块,破坏了系统的层次性。
2 支持广播通信,发送通知者不需要知道是谁接收了通知,而接收通知的对象可以选择处理或者不处理
缺点
1 意外的更新,由于通知者不知道观察者,在通知者看上去合理的操作,但可能会引起观察者的改变,从而导致很难查找的BUG
实现
1 创建目标与观察者对象的映射,最简单的方式是直接在目标对象中使用引用,但如果对象太多,开销会很大。如果用时间换空间的话,可以采用hash表,但增加了另外访问观察者的开销
2 观察多个目标,观察者知道是哪一个对象发来的通知,有时是很有必要的,因此可以扩展update把发送者的引用传过来
3 谁触发更新,有两个选择
1 目标对象的状态设定改变后自动调用Notify,
优点:客户不需要记住要在目标对象调用Notify
缺点:多个连续的操作会导致多个更新,效率较慢
2 让客户在合适的时候调用Notify.
优点:客户可以在一系列的改变后在调用Notity,避免不必要的中间状态的更新,效率较高
缺点:由客户控制更新的时机,有可能客户会忘记调用
4 对以删除的对象的悬挂调用,当删除一个对象时,该对象可能被其他对象引用,应该通知他的所有观察者,使所有的引用复位
5 在发出通知时确保目标的状态自身是一致的,因为观察者在更新其状态时需要查询目标的当前状态
6 避免特定与观察者的更新协议–推/拉模型
推模型:目标状态改变后就向观察者发出通知,不管观察者需要与否
缺点:观察者可能难以复用
拉模型:观察者主动向目标询问
缺点:效率很差
7 显示指定感兴趣的操作,观察者注册一个感兴趣的操作,当这个操作完成后通知观察者,可以提高效率
8 封装复杂的更新语义,当观察者的依赖关系比较复杂的时候,需要一个更改管理器,当所有的依赖更改都完成后才通知,不是每个目标都通知
具体的责任:
1 负责管理观察者与目标之间的引用关系
2 定义一个特定的更新
3 更加目标的请求,更新所有依赖与这个目标的更新
9 结合目标类和观察者类,将观察者和目标结合在一个类中,可以不使用多重继承
例子:
// ObserverModel.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include "Boss.h"
#include "StockObserver.h"
#include "NbaObserver.h"
int _tmain(int argc, _TCHAR* argv[])
{
Boss* boss = new Boss;
StockObserver* sObserver = new StockObserver("