观察者模式

稀土 2018-05-29

1)首先,我们要实现一个气温显示系统,有三个布告板需要相同的更新数据(温度、湿度、气压),由一个WeatherData类提供更新数据,要求每当数据更新时,要通知到三个布告板。最后还要有一个可扩展的第四个布告板,供其他开发人员建立定制的布告板。


一个错误的实现:

观察者模式

在这个错误的实现中:

  • 我们是针对具体实现编程,而非针对接口
  • 对于每个新的布告板,我们都得修改代码
  • 我们无法再运行时动态地增加或删除布告板
  • 我们尚未封装改变的部分

观察者模式与报纸的订阅非常相似:

  1. 报社的业务就是出版报纸
  2. 向某家报社订阅报纸,只要他们有新报纸出版,就会给你送来。只要你是他们的订户,你就会一直收到新报纸
  3. 当你不想再看报纸的时候,取消订阅,他们就不会再送新报纸来
  4. 只要报社还在运营,就会一直有人(或单位)向他们订阅报纸或取消报纸

观察者模式=出版者+订阅者

只是在观察者模式中,出版者改称为“主题”(Subject),订阅者改称为“观察者”(Observer)

观察者模式观察者模式观察者模式

2)定义观察者模式


观察者模式定义了一系列对象之间的一对多关系。

当一个对象改变状态,其他依赖者都会受到通知。

观察者模式观察者模式

实现观察者模式的方法不止一种,但是以包含Subject与Observer接口的类设计的做法最常见。

3)实现观察者模式


因为布告板的差异性,我们需要一个共同的接口,让布告板的类(可能不一样)都去实现相同的接口,好让WeatherData对象能够知道如何把观测值送给它们。

定义观察者模式类图:

观察者模式

设计气象站:

观察者模式观察者模式

3)松耦合的威力


当两个对象之间松耦合,它们依然可以交互,但是不太清楚彼此的细节。

观察者模式提供了一种对象设计,让主题和观察者之间松耦合。

为什么呢?

关于观察者的一切,主题只知道观察者实现了某个接口(也就是Observer接口)。主题不需要知道观察者的具体类是谁、做了些什么或其他任何细节。

任何时候我们都可以增加新的观察者。因为主题唯一依赖的东西是一个实现Observer接口的对象列表,所以我们可以随时增加观察者。事实上,在运行时我们可以用新的观察者取代旧的观察者,主题不会受到任何影响。同样的,也可以在任何时候删除某些观察者。

有新类型的观察者出现时,主题的代码不需要修改。加入我们有个新的具体类需要当观察者,我们不需要为了兼容新类型而修改主题的代码,所有要做的是在新的类里实现此观察者接口,然后注册为观察者即可。主题不在乎别的,它只会发送通知给所有实现了观察者接口的对象。

我们可以独立地复用主题或观察者。如果我们在其他地方需要使用主题或观察者,也可以轻易地复用,因为二者并非紧耦合。

改变主题或观察者其中一方,并不会影响另一方。因为两者是松耦合的,所以只要他们之间的接口仍被遵守,我们就可以自由地改变他们。

松耦合的设计之所以能让我们建立有弹性的OO系统,能够应对变化,是因为对象之间的互相依赖降到了最低。

观察者模式

4)实现气象站


观察者模式观察者模式观察者模式观察者模式观察者模式观察者模式观察者模式观察者模式观察者模式

5)观察者模式的“推(push)”和“拉(pull)”


到此为止,我们已经从无到有的完成了观察者模式。

仔细看我们实现的观察者模式会发现,所有的信息都是由主题“推(push)”给观察者的;

还有一种观察者模式是通过观察者从主题那边“拉(pull)”过来的,当然这需要主题设置相关的getter()方法。

6)Java内置的观察者模式


java.util包(package)内包含最基本的Observer接口与Observerable,这和我们的Subject接口与Observer接口很相似。Observer接口与Observerable类使用上更方便,因为许多功能都已经事先准备好了,你甚至可以使用推(push)或拉(pull)的方式传送数据。

这是修改后的气象站OO设计:

观察者模式观察者模式观察者模式观察者模式观察者模式观察者模式观察者模式观察者模式观察者模式观察者模式观察者模式观察者模式观察者模式

7)小结


OO基础:

  • 抽象
  • 封装
  • 多态
  • 继承

OO原则:

  • 封装变化
  • 多用组合、少用继承
  • 针对接口编程,不针对实现编程
  • 为交互对象之间的松耦合设计而努力

OO模式:

  • 策略模式——定义算法族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化独立于使用算法的客户
  • 观察者模式——在对象之间定义一对多的依赖,这样一来,当一个对象改变状态,依赖它的对象都会收到通知,并自动更新

要点:

  • 观察者模式定义了对象之间一对多的关系
  • 主题(也就是可观察者)用一个共同接口来更新观察者
  • 观察者和可观察者之间用松耦合方式结合(loosecoupling),可观察者不知道观察者的细节,只知道观察者实现了观察者的接口
  • 使用此模式时,你可以从被观察者处推(push)或拉(pull)数据(然而,推的方式被认为更“正确”)
  • 有多个观察者时,不可依赖特定的通知次序
  • Java有多种观察者模式的实现,包括通用的java.util.Observable
  • 要注意java.util.Observable带来的一些问题
  • 如果有必要的话,可以实现自己的Observable,这并不难
  • Swing大量使用观察者模式,许多GUI框架也是如此
  • 此模式也被应用在许多地方,比如:JavaBeans、BMI

参考书目:《Head First 设计模式》

相关推荐