目录
- 使用场景
- 参与者
- 协作
- 效果
- 实现
- 类图
观察者(Observer)又被称为 发布-订阅模式,是一种对象行为模式,定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时所有依赖于它的对象都得到通知并自动更新。通知的发布者(目标)发出通知时并不需要知道谁是它的观察者。可以有任意数量的观察者订阅并接收通知。
使用场景
- 当一个抽象模型有两个方面,其中一个方面依赖于另一方面,将这二者封装在独立的对象中以使他们可以各自独立地改变和复用
- 当对一个对象的改变需要同时改变其他对象,而不知道具体有多少对象要改变
- 当一个对象必须通知其他对象,而它又不能假定其他对象是谁,换言之,你不希望这些对象是紧密耦合的
参与者
- Subject(目标):
- 目标知道它的观察者,可以有任意多个观察者观察同一个目标
- 提供注册和删除观察者对象的接口
- Observer(观察者):为那些在目标发生改变时需要获得通知的对象定义一个更新接口
- ConcreteSubject(具体目标):
- 将有关状态存入各ConcreteObserver对象
- 当它的状态发生改变时,向它的各个观察者发出通知
- ConcreteObserver(具体观察者)
- 维护一个指向ConcreteSubject对象的引用——需要这个引用做什么?在哪些场合下在Observer中需要用到Subject对象?
- 存储有关状态,这些状态应与目标的状态保持一致
- 实现Observer的更新接口以使自身状态与目标状态保持一致
协作
- ConcreteSubject发生任何可能导致其观察者状态不一致的改变时,它将通知到它的各个观察者
- 在得到一个具体目标的改变通知后,ConcreteObserver对象可向目标对象查询信息(这一步有什么意义)。ConcreteObserver使用这些信息以使它的状态与目标对象的状态一致
- 注意:
- 发出改变请求的Observer对象并不立即更新,而是将其推迟到它从目标得到一个通知之后。
- Notify不总是由目标对象调用,它也可被一个观察者或其他对象调用
效果
Observer模式允许你独立的改变目标和观察者,可与单独复用目标对象而无需同时复用其观察者,反之亦然。它也可以在不改动目标和其他观察者的前提下增加观察者。
- 目标和观察者之间的抽象耦合。一个目标仅仅知道它有一系列观察者,每个都符合抽象的Observer类的接口,目标和观察者之间的耦合是抽象的和最小的
- 支持广播通信。目标对象不关心有多少对象对自己关心,它唯一的责任就是通知它的各个观察者;处理还是忽略一个通知取决于观察者
- 意外的更新:因为一个观察者并不知道其他观察者的存在,他可能对改变目标的最终代价一无所知,因此可能会造成问题
实现
- 创建目标到其观察者之间的映射
- 最简单的方法是显式的在目标中保存对观察者的引用,但当目标很多而观察者较少时这样的存储代价太高
- 用时间换空间:用一个关联查找机制来维护目标到观察者的映射——待实现
- 观察多个目标:某些情况下一个观察者依赖多个目标可能是有意义的,例如一个表格对象可能依赖于多个数据源——多个目标时应该需要区分是哪个目标发出的通知
- 谁触发更新:
- 由目标对象的状态设定操作在改变目标对象的状态后自动调用Notify。这种方法的优点是不需要客户调用Notify,缺点是连续的操作会产生多次连续的更新,可能效率会比较低,目前大部分都是用这种方式通知
- 客户负责调用Notify,优点是客户可以在一系列的状态完成后再一次性触发更新,避免了不必要的中间更新;缺点是给客户增加了触发更新的责任
- 对已删除目标的悬挂引用。删除一个目标时应注意不要在其观察者中遗留对该目标悬挂引用。一种避免悬挂引用的方法是当一个目标被删除时,让它通知它的观察者将对该目标的引用复位。一般来说不能简单地删除观察者,因为其他对象可能会引用它们
- 在发出通知前确保目标的状态自身是一致的。因为观察者在更新其状态的过程中需要查询目标的当前状态。当Subject的子类调用继承的该项操作时很容易无意中违反这条自身一致原则
- 避免特定于观察者的更新协议—推/拉模型:广播出去的信息量可能很大,也可能很少
- 推模型:目标向发送者发送关于改变的详细信息,而不管他们是否需要;
- 推模型假定目标知道一些观察者的需要的信息
- 推模型可能使得观察者相对难以复用
- 拉模型:目标除了最小通知外什么也不送出,而在此之后由观察者显式的向目标询问细节。
- 拉模型强调的是目标不知道它的观察者
- 拉模型可能效率较差,因为观察者对象需在没有目标对象帮助的情况下确定什么改变了
- 推模型:目标向发送者发送关于改变的详细信息,而不管他们是否需要;
- 显式的指定感兴趣的改变:可以扩展目标的注册接口让各观察者注册仅对特定事件感兴趣
- 封装更复杂的语义:当目标和观察者间的依赖关系特别复杂时可能需要一个维护这些关系的对象,称这样的对象为更改管理器,它的目的是尽量减少观察者反映其目标的状态变化所需的工作量。三个责任:
- 将一个目标映射到它的观察者并提供一个接口来维护这个映射,这就不需要由目标来维护对其观察者的引用,反之亦然
- 它定义一个特定的更新策略
- 根据一个目标的请求,更新所有依赖于这个目标的观察者
- 谁来向目标注册观察者?可以是客户,也可以是Observer,书中的例子是Observer