Head First设计模式之观察者模式
tags: 设计模式
本章主要介绍观察者模式。观察者模式可以帮助对象知悉现况,不会错过对象感兴趣的事。对象甚至在运行时可决定是否要继续被通知。观察者模式是JDK中使用最多的模式之一。本章也会一并介绍一对多关系,以及松耦合。有了观察者,消息会更灵通。
1. 认识观察者模式
出版者+订阅者=观察者模式
观察者模式类似于报纸的订阅,只是名称不太一样:出版者改称为"主题"(Subject),订阅者改称为"观察者"(Observer)。
主题对象:主题对象管理某些数据
观察者对象:观察者订阅(注册)主题以便在主题数据改变时能够收到更新
当主题内的数据发生改变时,新的数据就会以某种形式送到观察者手上
2. 定义观察者模式
观察者模式定义了对象之间的一对多依赖,这样一来,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新。
实现观察者模式的方法不止一种,但是以包含Subject与Observer接口的类设计的做法最常见。
3. OO设计原则
为了交互对象之间的松耦合设计而努力
4. 松耦合
观察者模式提供了一种对象设计,让主题和观察者之间松耦合。
当两个对象之间松耦合,它们依然可以交互,但是不必清楚彼此的细节。
松耦合的设计能建立有弹性的OO系统,因为对象之间的互相依赖降到了最低。
5. Java内置的观察者模式
5.1 Observer接口和Observable类
Java API有内置的观察者模式。java.util包中包含最基本的Observer接口和Observable类,Observer接口和Observable类使用上更加方便,因为许多功能都已经事先实现过了。通过使用推(push)或拉(pull)的方式传送数据。
由于Java内置的支持,只需扩展(继承)Observable,并告诉它核实该通知观察者,剩下的事API会帮助你完成。
Observable类中的方法有:
- addObserver()
- deleteObserver()
- notifyObserver()
- setChanged()
Observable是一个"类",而非接口。Observable类会追踪所有的观察者,并通知它们。之前的"主题"(Subject),也称为"可观察者"(Observable)继承自Observable类。在继承的类(主题)中不需要再提供register()、remove()和notifyObservers()方法,因为已经从超类(Observable)中继承了。
5.2 Java内置的观察者模式
5.2.1 Java内置观察者模式与观察者模式的差别
主题扩展自Observable类,并继承了增加、删除、通知观察者的方法以及其他方法。
5.2.2 Java内置观察者模式的使用
-
将对象变成观察者:实现观察者接口(java.util.Observer),然后调用任何Observable对象的addObserver()方法。当不想再当观察者时,调用deleteObserver()方法即可。
-
可观察者(主题)传送数据:
-
利用扩展java.util.Observable类产生"可观察者"
-
调用setChanged()方法,标记状态已经改变
-
调用notifyObservers()或notifyObservers(Object arg)
如果想"推"(push)数据给观察者,就把数据当做数据对象传送给notifyObservers(Object arg)方法。否则,观察者就必须从可观察者对象中"拉"(pull)数据。
-
-
观察者接受数据:观察者实现更新的方法
update(Observable o,Object arg) //Observable o:主题(可观察者)本身当做第一个变量,使观察者知道是哪一个主题通知它的 //Object arg:传入notifyObservers()的数据对象。未说明则为空。
5.2.3 setChanged()方法
setChanged()方法用来标记已经改变的事实,让notifyObservers()方法知道当它被调用时应该更新观察者。如果调用notifyObservers()之前未调用setChanged()方法,则观察者就"不会"被通知。
setChanged()方法的伪代码:
setChanged(){
changed = true //setChanged()方法把changed标志设为true
}
//推数据
notifyObservers(Object arg){
//notifyObservers()方法只会在changed标为"true"时通知观察者
if(changed){
for every observer on the list{
call update(this,arg)
}
changed = false
//通知观察者之后,把changed标志设回false
//所以若未调用setChanged()方法,则changed默认为false,就不会通知观察者进行更新
}
}
//拉数据
notifyObservers(){
notifyObservers(null)
}
//clearChanged():将changed状态设置回false
//hasChanged():得到changed标志当前的状态值(true or false)
5.3 Java内置观察者模式的使用步骤
-
导入正确的Observable/Observer
import java.util.Observer; import java.util.Observavle;
-
继承Observable类
public class Xxx extends Observable{ //代码 }
-
调用setChanged()方法和notifyObservers()方法
public void xxx(){ setChanged(); //注意调用先后顺序,setChanged()在前,notifyObservers()在后 notifyObservers(); //notifyObservers(Object args); }
5.4 Java内置观察者模式的缺点
-
Observable是一个类,限制了Observable的复用潜力
- 因为Observable是一个类,所以必须设计一个类继承它。如果某类想同时具有Observable类和另一个超类的行为,就会陷入两难,毕竟Java不支持多重继承。
- 因为没有Observable接口,所以无法建立自己的实现,和Java内置的Observers API搭配使用,也无法将java.util的实现换成另一套做法的实现。
-
Observable将关键的方法保护起来了
在Observable API中,setChanged()方法被定义成protected。这意味着:除非继承自Observable,否则无法创建Observable实例并组合到自己的对象中。该设计违反了"多用组合,少用继承"这个设计原则。
6. 观察者模式如何遵循设计原则
6.1 设计原则:找出程序中会变化的部分,将其和固定不变的部分相分离
**在观察者模式中,会变化的是主题的状态,以及观察者的数目和类型。**使用此模式,可以改变依赖于主题状态的对象,而不必改变主题。
6.2 设计模式:针对接口编程,不针对实现编程
主题与观察者都使用接口:观察者利用主题的接口向主题注册,主题利用观察者接口通知观察者。这样既可以让两者之间正常运作,又同时具有松耦合的优点。
6.3 设计模式:多用组合,少用继承
观察者模式利用"组合"将许多观察者组合进主题中。对象之间的关系并非通过继承产生,而是在运行时利用组合的方式产生的。
7. 要点小结
- 观察者模式定义了对象之间一对多的关系
- 主题(可观察者)用一个共同的接口来更新观察者
- 观察者和主题(可观察者)之间用松耦合方式(loosecoupling)结合,可观察者不知道观察者的细节,只知道观察者实现了观察者接口
- 使用观察者模式时,既可从被观察者处推(push)数据,也可从被观察者处拉(pull)数据(推的方式更安全)
- 有多个观察者时,不可以依赖特定的通知次序