第14章 老板回来,我不知道——观察者模式
观察者模式
定义
观察者模式又叫发布-订阅模式。定义了一对多的依赖关系,多个观察者对象(比如订阅者)同时监听一个主题对象的状态(比如主题的发布),主题对象状态变化会通知观察者让它们自己更新自己。
适用场景:适用于一个对象改变,同时其它多个对象也需要根据此改变的状况,而且也不知道有多少种类与数目的对象(包含数目和种类)的状况。
实现
- 主题对象抽象类(Subject),包含抽象观察者列表存放所有观察者引用,增加、删除观察者列表的函数。
- 具体主题对象类(ConcreteSubject),包含当前主题状态,获取/设置状态的函数,以及状态变化时通知列表中所有观察者状态的函数。
- 观察者对象抽象类(Observer)只包含一个抽象的Update接口,供主题对象通知时调用通知相应观察者。
- 具体观察者对象类(ConcreteObserver)记录已知最新的主题对象状态,通过实现Update()函数根据观察者的通知(推送),来更新自己已知主题状态和执行相关动作。
- 客户端(Client)创建主题类对象,为主题类对象调用接口添加观察者,然后主题类对象通知观察者(以获得相应的结果)
观察者模式类图实现:
观察者模式客户端实现:
总结
优点:
去除与具体类之间的耦合,抽象出公共接口,这样双方仅仅依赖对方的抽象类。
(比如主题类中的列表只是抽象观察者类型,观察者中通过抽象主题类获得通知的状态)
缺点:
1.每个观察者对象都要有一个Update函数,供主题对象通知时调用,如果是已实现的观察者对象类没有Update接口,那么无法通知这样的对象(抽象观察者对主题者通知方式是调用Update()的依赖)。
2.已有实现的观察者类不是抽象观察者抽象类的子类,因无法改变实现,则无法被主题者的抽象观察类列表保存,导致也无法通知这样的对象(抽象主题者,对观察者类型必须继承抽象观察者的依赖)。
解决:进一步去除对固定方法和抽象类型的依赖,需要借助事件委托机制实现。
事件委托机制
定义
事件委托就是一个函数类型的可调用对象,可以通过向其中添加函数,然后直接调用对象就可调用到其中被添加的所有函数。
要求:委托搭载的函数必须具有同样的原型,即同样的参数和返回类型。
是先有的观察者模式,才出现的事件委托机制。
实现
- 主题类中,用事件委托对象(列表)替代抽象对象列表(事件委托类比如EventHandler Update;),去除了对观察者抽象类的依赖(即观察者不必继承抽象观察类了)。
- 主题通知的时候,只需要调用事件委托对象(列表)Update,去除了对函数必须固定成Update的依赖;
- 观察者类中,不必继承抽象观察者,也不必一定使用Update()这样的函数名(即观察者不必完全依赖主题者用于通知的Update()函数了),但只要保证其函数和委托对象的原型一致即可(这个还是要依赖的,抽象了函数名后,相关的通知导致相关的动作,但是不限制动作的名称这是合理的)。
- 客户端中,创建主题类对象,创建一个委托对象并将观察者对象的对应动作添加至其中(而非对象本身),然后将委托对象传给主题对象(比如:用“主题对象.Update+=new EventHandler(观察者函数)” 的方式,创建一个委托)。最后,主题对象状态改变,并调用其内的委托对象,就达到了观察者的目的。
总结
加入事件委托对象的观察者模式特点是,观察者和主题者相互直接没有依赖,委托对象实质是深入对函数进行了抽象,使得仅有的依赖就是观察这的动作原型要与主题者调用的委托时间对象通知函数原型保持一致。(其实,主题者完全不用依赖观察者,只管自己的状态更新以及机械的通知;观察者是无法完全不依赖主题者的,因为毕竟它要观察主题的变化以更新自己,但是它仅保证接口的原型一致就行,名称无所谓)