设计模式之观察者模式
从一个气象监测应用说起:
我们想做一个气象应用,有一个WeatherData对象,该对象由气象站的各种感应装置提供温度,湿度,气压数据 来初始化。同时我们还要设立3个布告板,来根据天气数据 显示不同的内容。分别是:目前状况,气象统计,简单的预报。 同时我们还希望可以扩展该接口,即第三方可以根据数据来实现自己的布告板。
背景已经交代清楚了,这其实是一个一对多的关系,即多个布告板全都依赖WeatherData对象,根据WeatherData对象做出自己的改变。下面是一个错误的示范
虽然它能够满足大部分条件,但是它不具备扩展性,每次添加新的布告板时,我们都需要修改代码,我们是针对接口编程而不是针对具体实现编程,布告板没有实现一个共同的接口,而且我们无法在运行的时候动态的删除或添加布告板(只能在measurementsChanged()方法中删除对该布告板的更新。所以为了搞定这个问题,我们引出观察者模式:
认识观察者模式:
观察者模式和报纸和杂志的订阅很像,报社出版报纸,向某家报社订阅报纸,只要他们有新报纸出版,就会给你送来,只要你是他们的订户,你就会一直收到新报纸。当你不想在看报纸时,取消订阅,他们便不会再送新报纸给你。只要报社还在运营,就会一直有人向他们订阅报纸或取消订阅报纸。
出版者 + 订阅者 = 观察者模式
我们可以将他们改下名称, 出版者改为“主题”,订阅者改为 “观察者”(《Head First 设计模式》里面是这样子的)。
观察者模式的定义:
观察者模式定义了对象之间的一对多依赖,这样一来,当一个对象改变状态时,它的所有依赖者都将会收到通知并自动更新。
好了,我们可以根据这个模式来解决我们的实际问题了。
在我们的问题中,布告板会随着WeatherData的变化而得到更新,所以很明显WeatherData是主题,而布告板是观察者。实现观察者模式的方法不只一种,但是以包含Subjec与Observer接口的最为常见,之后会讲到JAVA中自带的观察者模式。下面是具体实现:
|
|
此实现有缺点。一个观察者可以有多个主题,观察者可根据主题来实现不同的update(),例如我们可以将update()改成update(Subject subject,Object obj)后面的是Object是主题给观察者的数据对象。
其实在这里观察者得到数据的方法是由主题推送的,观察者无法通过自身得到更新的数据,我们可以在主题中实现观察者访问数据的接口
|
|
java内置的观察者模式:
JAVA API有内置的观察者模式,在java.util包中,包含最基本的Observer接口和Observable类(主题的另一种说法,“可观察者”),里面的方法请参考JDK文档,在此我们就直接实现:
|
|
java.util.Observable的黑暗面:
可能你也注意到了,Observable是一个类,而不是一个接口,而且它自己也没有实现一个接口,因此在很多方面限制了它的使用与复用,不符合我们设计原则,针对接口编程而不是针对实现编程。
首先Observable是一个类,你必须设计一个类继承它,如果某类想同时具有另一个超类的行为,则会陷入两难,因为JAVAv不支持多继承。再者,Observable中的setChanged()方法是protected的,这就意味着除非你继承自Observable,否则你无法创建Observable实例并组合到你自己的对象里来。