设计模式-观察者模式

来源:互联网 发布:米思米标准件软件 编辑:程序博客网 时间:2024/06/07 08:38

复习下head first书中的观察者模式

1.问题背景

你的公司刚刚接到项目,负责建立一个气象站。此系统的三个部分分别是气象站(获取实际气象数据的物理装置)、WeatherData对象(负责追踪来自气象站的数据,并更新布告板),还有三个布告板。我们的布告板上实时显示气象站测得的温度、湿度和气压,同时客户希望我们公布一组API,能让其他人使用我们的数据定制自己的布告板。项目结构图如下所示:

WeatherData对象知道如何跟物理气象站联系,以取得最新的数据,同时会负责实时更新布告板的显示。我们的工作就是建立应用,利用WeatherData对象取得数据,并更新三个布告板。当然图中已经画出了一个布告板,剩下的两个也是类似的。
WeatherData对象有取得温度、湿度、气压的三个getter方法。同时当新的数据产生时,measurementChanged()方法会被调用。

2.尝试解决


注意这里我们在WeatherData对象里面拥有三个布告板的实例变量,然后是measurementChanged方法中分别调用三个布告板对象的update方法,更新布告板的数据。每个布告板的update方法中会调用各自的display方法,更新显示,这个解决办法怎么样?
其实这个实现方法不见得很有弹性,而且违反了我们上一章讲的几个设计原则:针对接口编程,不针对实现编程,我们调用的是三个实例对象布告板的方法,而不是一个接口。针对实现编程会导致今后我们增加或者删除布告板必须修改程序;我们也没有把变换的部分(各个布告板的增删),和不变的部分(weatherDate对象)分开。

3.观察者模式闪亮登场

观察者模式定义了一对多的依赖,这样当一个对象改变状态时,所有依赖者都会收到通知并自动更新。关于观察者模式我们必须知道两个术语:主题和观察者。
上面是主题和观察者的关系图。对于我们的问题中,主题就是WeatherData对象,观察者就是各个布告板。主题维护一个变长的观察者类型的向量。每个观察者想实时取得气象站的数据都必须向主题注册,注册以后主题把这个观察者放到自己的向量中,这个问题中,主题WeatherData对象的measurementChanged()方法会遍历自己维护的向量,通知每一个布告板观察者。如果观察者不想继续观察,可以通知主题移除自己的观察者身份。
注意:主题用于一组观察者对象作为属性;而观察者拥有一个主题对象作为属性,并在自己的构造方法参数中传入这个主题,调用相应的方法向主题注册观察者或者取消观察者身份。
现在我们的气象站项目结构仍然是三部分:气象站观测设备+weatherDate对象(主题)+布告板(观察者)。下面是主题和观察者接口的代码:



下面是我们的WeatherDate类和布告板类的具体实现:


这里我们的主题使用ArrayList维护自己的观察者列表。而布告板的update方法会调用display方法,保证我们在主题中更新观察者数据的同时,数据可以立马同步显示。
观察者模式其实有两种,我们这种是“推”的形式,主题自动向观察者推送消息,观察者自动更新。还有另外一种“拉”的形式,主题向观察者提供每个属性的getter方法供观潮者在需要的地方自行调用。java内置的观察者模式两种形式都是支持的。

4.java内置的观察者模式

java.util里面有最基本的Observer(观察者)接口和Observable(主题)类,下面是他们的关系图。

在java版本中,观察者实现观察者接口Observer,并调用Observable对象的addObserver()方法,不想当观察者时,调用deleteObserver方法;主题继承Observable类,也被称为可观察者,可观察者里面有状态标记变量用来标记数据是否有变化,调用setChanged()方法改变标记状态(true或者false),然后调用notifyObservers()(调用的时候可以在参数中指定通知某一个观察者,如果没有参数默认通知全部观察者)。
观察者实现了更新的方法:update(Observable o, object arg),如果我们使用“推”的模式,可以把数据当成对象传送给notifyObserver(arg)方法。否则就必须从可观察者哪里“拉”数据。

4.观察者模式总结

我们注意到Observable类是一个类而不是接口,甚至没有实现一个借口,因为java只支持单继承,所以这对我们来说不是个好消息。观察者必须有update方法,而主题必须拥有注册观察者和删除观察者身份的方法,还必须有数据改变时自动调用的方法。

观察者模式广泛用于各种键盘监听和鼠标监听。比如安卓的Button的setOnClickListener()方法是用来设置监听器的。Button的超类的View里面定义了setOnClickListener()方法,方法的参数是一个OnClickListener对象,所以安卓的各个控件都是可观察者,而onClickLisktener或者OnFocusListener等对象是观察者。每次Button的事件都会根据事件的类型通知给相应的Listener来处理。各种控件自动维护所有的Listener列表,按事件不同通知相应的监听器执行相应的操作,但是这个过程细节用户是不可见的。

当然,不只是android,javascript等等面向对象的语言基本上都广泛的使用了观察者模式,只是每个观察者模式稍有不同。