观察者模式
提出问题
将一个系统分割成一系列相互协作的类有一个常见的副作用,需要维护相关对象间的一致性。不希望为了维持一致性而使各个类紧密耦合,这样降低了它们的可重用性
问题案例
应用
适用性
- 当一个抽象模型有两个方面,其中一个方面依赖于另一个方面。将二者封装在独立的对象中以使它们可以格子独立地改变和复用
- 当对一个对象的改变需要同时改变其他对象,而不知道具体有多少对象有待改变
- 当一个对象必须通知其他对象,而它又不能假定其他对象是谁,即不希望对象是紧耦合的。
案例1
考虑报纸和杂志的订阅
- 报社的业务就是出版报纸
- 向某家报社订阅报纸,只要他们有新的报纸出版,就会给你送来,只要你是他们的订户,你就会一直收到报纸
- 当你不想再看报纸时,取消订阅,他们就不会再送新报纸过来
- 只要报社还在运营,就会一直有人向他们订阅报纸或取消订阅报纸
在观察者模式当中:出版社为“主题”,订阅者为“观察者”
案例2
考虑Excel里面的图表与数据
- 定义数据的类和负责界面表示的类可以各自独立地复用,也可以一起工作
- 当数据改变时,柱状图、表格等会立即改变。但是表格对象和柱状图似乎不知道对方的存在,可以根据需要单独复用表格和柱状图
- 即意味着表格对象和柱状图都依赖于数据对象,因此数据对象的任何状态改变都应立即通知它们。而且对于数据可以有任意数目的不同的用户界面
基础概述
是什么
观察者模式:定义了对象之间的一对多依赖,这样一来,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新
- 可观察者(主题)与观察者,一旦主题的状态发生改变,所有的观察者状态都会得到通知,作为对通知的响应,观察者都会将查询目标的状态与自己的状态同步。
- 帮助对象知晓现状,不会错过该对象感兴趣的事情,对象甚至可以在运行时可决定是否要继续被通知
- 定义了一种对象之间的一对多依赖,这样一来,当一个对象改变时,它的所有依赖者都会收到通知并自动更新
一对多的依赖关系
- 主题是真正拥有数据的人 ,观察者是依赖者,只是更新数据,但并不控制数据
- 主题是具有状态的对象,并可以控制状态,观察者使用状态,依赖主题告诉他们状态何时改变
松耦合
- 对于观察者的一切,主题只知道观察者实现了某个接口,主题并不需要知道观察者具体是谁。
- 当出现新的观察者或新的具体类,只需要实现观察者接口,并将其注册到主题上即可
- 改变主题与观察者,并不会影响另一方,只要还遵循接口,便可以自由地改变
分类
协作
结构
参与者
- Subject:主题对象
- 主题知道它的观察者,可以有任意多个观察者观察同一个目标
- 提供注册和删除观察者对象的接口
- Observer:观察者对象,订阅(注册)主题,以便在主题数据改变时能受到更新
- ConcreteSubject:具体目标
- 将有关的状态存入各个ConcreteObserver中,当状态改变时通知观察者
- ConcreteObserver:具体观察者
- 维护一个指向ConcreteSubject的引用
- 存储有关状态,这些状态与Subject的状态一直,并实现Observer的更新接口,以使得一旦数据改变,新的数据便会以某种形式送到观察者手中
协作
- 类关系
- 逻辑关系
- 当ConcreteSubject发生任何可能导致其观察者与其本身状态不一致的改变时,它将通知它的各个观察者
- 当得到一个具体Subject的改变通知后,ConcreteObserver可以向Subject查询信息,并使用这些信息以使得它的状态与Subject的状态一致
权衡
分类
结构
效果(优缺)
实现
实现有两种情景
- 主题进行推送通知
- 观察者拉取通知
在之后的手动实现中,只考虑推送通知,而拉取通知有Java的内置实现
实现步骤
IDE支持
java.util包中包含有最基本的Observer(观察者)接口和Observvable(主题)类,并且可以进行拉取操作
如何获得观察者
实现Observer接口,然后使用主题类进行addObserver(),删除则remove
主题如何推送通知
案例1
相关模式
进阶
使用Lambda
反省总结
- 观察者模式定义了对象间一对多的关系
- 主题(可观察者)用一个共同的接口更新观察者
- 观察者与可观察者之间松耦合,二者并不清楚内部的细节
- 使用此模式,可以从观察者处推或拉数据
- 有多个观察者时,不可以依赖特定的观察次序