认识观察者模式
什么是观察者模式呢?我们先看看看报纸和杂志的订阅是怎么回事:
- 报社的业务就是出版报纸
- 向某家报社订阅报纸,只要报社有新的的报纸出版,就会给你送来,只要你是他们的客户,你就会一直收到新的报纸
- 当你不想再看报纸的时候,可以取消订阅,他们就不再送新的报纸过来。
- 只要报社还一直在运营,就会一直有人(或单位)向他们订阅报纸或取消订阅报纸
如果你了解报纸的订阅是怎么回事,其实就知道观察者模式是怎么回事,只是名称不太一样: 出版社改为”主题(Subject)”,订阅者改称为”观察者(Observer)”。
看下面的图例:
案例
有一个气象监测的应用,由WeatherData对象负责追踪目前的天气状况(温度、湿度、气压)。建立一个应用,有三种布告板,分别显示目前的状况、气象统计、天气预测。当WeatherData对象获取最新的测量数据时,三种布告必须实时更新。
一个错误的示范
上面的要求,假设WeatherData中有以下的属性和方法:
上面的方式实现从WeatherData中的测量变化后得到相对应的数据,再通过三种布告的实例,将数据传入进行。这种做法好像是可以达到WeatherData数据更新,三种布告数据也同步更新的功能,但是这样的实现有什么不对吗?请看下面的分析:
- measuremntsChanged中的三个实例变量中,针对的是具体实现编程,会导致我们以后再增添或减少布告的时候必须修改程序了。
- 三种实例都有一个公用的update, 像这样的,应该抽取出来,封装变化的部分。
- 我们无法在运行时动态的增加或删除布告。
正确的实例:
定义观察者模式
观察者模式定义了对象之间的一对多依赖,这样一来,当一个对象改变状态的时候,它所依赖者都会收到通知并自动更新。
观察者模式提供了一种对象设计,让主题和观察者之间松耦合。为什么呢?
因为关于观察者的一切,主题只知道观察者实现了某个接口(也就是Observer接口),主题不需要知道观察者的具体类是谁、做了什么或其他任何细节。
任何时候我们都可以增加新的观察者,因为主题唯一依赖的东西是一个实现Observer接口的对象列表,,所以我们可以随时增加观察者,事实上,在运行时我们可以用新的观察者取代现有的观察者,主题不会受到任何影响。同样的,也可以在任何的时候删除某些观察者。
有新类型的观察者出现的时,主题代码不需要修改。假如我们有个新的具体类需要当观察者,我们不需要为了兼容新类型而修改主题的代码,所有要做的就是在新的类里实现此观察者接口,然后注册为观察者即可,主题不在乎别的,它只是会发送通知给所有实现了观察者接口的对象。
松耦合的设计之所以能让我们建立有弹性的OOP系统,能够应对变化,是因为对象之间的相互依赖降到了最低。
设计气象站
实现
观察者接口:
主题接口:
布告接口:
WeatherData中实现主体接口
当前状况的布告板实现:
预测布告实现:
测试:
上面测试代码输出如下图:
使用Java内置的观察者模式
Java API中有内置的观察者模式。java.util包中含有最基本的Observer接口与Observable类,这个和我们的Subject接口与Observable接口很相似。Observer接口与Observable类使用上更方便,因为许多功能都已经事先准备好了。如下图:
Java内置的观察者模式如何运作
Java内置观察者的运作模式是怎么样的?
1、如何把对象变成观察者?
和以前一样, 实现观察者接口(java.uitl.Observer),然后调用任何Observable对象的addObserver()方法,不想再当观察者时,调用deleteObserver()方法就可以。
2、可观察者(主题)如何送出通知
1、先调用setChanged()方法,标记状态已经改变的事实。
2、然后调用两种notifyObservers()方法中的一个notifyObservers()或notifyObservers(Object arg)。
3、观察者如何送出通知
同和我们写的一样,观察者实现了更新的方法,但是方法签名不太一样: update(Observale o, Object arg), 参数中,主题本身当做一个变量,好让观察者知道是哪一个主题通知他的。第二个参数是可观察者中传入notifyObservers(Object arg)的数据对象.
如果你想”推”(push)数据给观察者,你可以把数据当做数据对象传递给notifyObserver(arg) 方法,否则,观察者就必须从可观察者对象中”拉”(pull)数据。如何“”拉”数据?下面的实现就知道了。
利用Java内置类实现观察者模式案例
可观察者WeatherData:
当前状况布告(观察者):
测试:
上面测试代码输出如下图:
总结
- 观察者设计模式——在对象之间定义一对多的依赖,这样一来,当一个对象改变状态,依赖它的对象都会收到通知,并自动更新。
- 观察者模式定义了对象之间一对多的关系。
- 主题(也就是可观察者)用一个共同的接口来更新观察者。
- 主题(也就是可观察者)用一个共同的接口来更新观察者。
- 使用此模式时,你可以从被观察者处(push)。 或拉(pull)数据(然而。推的方式被认为更“正确”)。
- 有多个观察者时,不可以依赖特定的通知次序。
- ava有多个观察者模式的实现,包括了通用的java.util.Observable。
- 要注意java.util.Observale实现上所带来的问题:
Observale 是一个类,这个可不是一件好的事情,因为它是一个类,你必须设计一个类继承它,如果某个类想同时具有Observable类和另外一个超类的行为。就会陷入两难,毕竟Java不支持多继承。这限制了Observable的复用潜力(增加复用潜力正是我们使用模式的最原始的动机)。
再看看Observable API, 会发现setChanged()方法被保护起来了(被定义成protected). 那么这意味着:除非你继承自Observale,否则你是无法创建Observale 实例并组合到你自己的对象中来。这个设计规则违反了第二个设计原则:多用组合,少用继承。 - Swing大量使用观察者模式,许多GUI框架也是如此。
- 此模式被应用在许多地方,例如:JavaBean、RMI。