观察者模式

观察者模式

提出问题

将一个系统分割成一系列相互协作的类有一个常见的副作用,需要维护相关对象间的一致性。不希望为了维持一致性而使各个类紧密耦合,这样降低了它们的可重用性

问题案例

应用

适用性

  • 当一个抽象模型有两个方面,其中一个方面依赖于另一个方面。将二者封装在独立的对象中以使它们可以格子独立地改变和复用
  • 当对一个对象的改变需要同时改变其他对象,而不知道具体有多少对象有待改变
  • 当一个对象必须通知其他对象,而它又不能假定其他对象是谁,即不希望对象是紧耦合的。

案例1

考虑报纸和杂志的订阅

  • 报社的业务就是出版报纸
  • 向某家报社订阅报纸,只要他们有新的报纸出版,就会给你送来,只要你是他们的订户,你就会一直收到报纸
  • 当你不想再看报纸时,取消订阅,他们就不会再送新报纸过来
  • 只要报社还在运营,就会一直有人向他们订阅报纸或取消订阅报纸

在观察者模式当中:出版社为“主题”,订阅者为“观察者”

案例2

考虑Excel里面的图表与数据

  • 定义数据的类和负责界面表示的类可以各自独立地复用,也可以一起工作
  • 当数据改变时,柱状图、表格等会立即改变。但是表格对象和柱状图似乎不知道对方的存在,可以根据需要单独复用表格和柱状图
  • 即意味着表格对象和柱状图都依赖于数据对象,因此数据对象的任何状态改变都应立即通知它们。而且对于数据可以有任意数目的不同的用户界面

基础概述

是什么

观察者模式:定义了对象之间的一对多依赖,这样一来,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新

  • 可观察者(主题)与观察者,一旦主题的状态发生改变,所有的观察者状态都会得到通知,作为对通知的响应,观察者都会将查询目标的状态与自己的状态同步。
  • 帮助对象知晓现状,不会错过该对象感兴趣的事情,对象甚至可以在运行时可决定是否要继续被通知
  • 定义了一种对象之间的一对多依赖,这样一来,当一个对象改变时,它的所有依赖者都会收到通知并自动更新

一对多的依赖关系

  • 主题是真正拥有数据的人 ,观察者是依赖者,只是更新数据,但并不控制数据
  • 主题是具有状态的对象,并可以控制状态,观察者使用状态,依赖主题告诉他们状态何时改变

松耦合

  • 对于观察者的一切,主题只知道观察者实现了某个接口,主题并不需要知道观察者具体是谁。
  • 当出现新的观察者或新的具体类,只需要实现观察者接口,并将其注册到主题上即可
  • 改变主题与观察者,并不会影响另一方,只要还遵循接口,便可以自由地改变

分类

协作

结构

1559290104936

参与者

  • Subject:主题对象
    • 主题知道它的观察者,可以有任意多个观察者观察同一个目标
    • 提供注册和删除观察者对象的接口
  • Observer:观察者对象,订阅(注册)主题,以便在主题数据改变时能受到更新
  • ConcreteSubject:具体目标
    • 将有关的状态存入各个ConcreteObserver中,当状态改变时通知观察者
  • ConcreteObserver:具体观察者
    • 维护一个指向ConcreteSubject的引用
    • 存储有关状态,这些状态与Subject的状态一直,并实现Observer的更新接口,以使得一旦数据改变,新的数据便会以某种形式送到观察者手中

协作

  • 类关系
  • 逻辑关系
    • 当ConcreteSubject发生任何可能导致其观察者与其本身状态不一致的改变时,它将通知它的各个观察者
    • 当得到一个具体Subject的改变通知后,ConcreteObserver可以向Subject查询信息,并使用这些信息以使得它的状态与Subject的状态一致

权衡

分类

结构

效果(优缺)

实现

实现有两种情景

  • 主题进行推送通知
  • 观察者拉取通知

在之后的手动实现中,只考虑推送通知,而拉取通知有Java的内置实现

实现步骤

IDE支持

java.util包中包含有最基本的Observer(观察者)接口和Observvable(主题)类,并且可以进行拉取操作

如何获得观察者

实现Observer接口,然后使用主题类进行addObserver(),删除则remove

主题如何推送通知

案例1

相关模式

进阶

使用Lambda

反省总结

  • 观察者模式定义了对象间一对多的关系
  • 主题(可观察者)用一个共同的接口更新观察者
  • 观察者与可观察者之间松耦合,二者并不清楚内部的细节
  • 使用此模式,可以从观察者处推或拉数据
  • 有多个观察者时,不可以依赖特定的观察次序

参考