Java设计模式(十)
------------观察者模式
引入
我们这里利用《Head.First.设计模式》中的那个气象站的经典的例子坐引。
我们需要建立一个该气象站,它必须建立在我们专利申请中的WeatherD ata对象上,由WeatherData对象负责追踪目前的天气状况(温度、湿度、气压)。我们还要建立一个应用,有三种布告板,分别显示目前的状况、气象统计及简单的预报。当WeatherObject对象获得最新的测量数据时,三种布告板必须实时更新。
而且,我们还希望它是一个可以扩展的气象站,也就是允许其他开发人员可以写出自己的气象布告板,并插入此应用中。
好了,下面的例子我们都将围绕这个例子进行。
定义与角色
定义
观察者模式定义了对象之间的一对多依赖,这样一来,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新。
角色
抽象主题角色
publicinterfaceSubject {
publicvoidaddObserver(Observer o);
publicvoidremoveObserver(Observer o);
publicvoidnotifyObserver();
}
具体主题角色
publicclassWeatherDataimplementsSubject {
Listlisteners=newArrayList();
publicintgetTemperature() {
return0;
}
publicdoublegetHumidity() {
return0.53;
}
intgetPressure() {
return30;
}
//对外暴露接口
publicvoidmeasurementsChanged() {
this.notifyObserver();
}
@Override
publicvoidaddObserver(Observer o) {
//TODOAuto-generated method stub
listeners.add(o);
}
@Override
publicvoidnotifyObserver() {
//TODOAuto-generated method stub
intt =this.getTemperature();
doubleh =this.getHumidity();
intp =this.getPressure();
for(inti = 0; i
Observer listener =listeners.get(i);
listener.onWebtherChange(t, h, p);
}
}
@Override
publicvoidremoveObserver(Observer o) {
//TODOAuto-generated method stub
listeners.remove(o);
}
}
抽象观察者角色
publicinterfaceObserver {
publicvoidonWebtherChange(intt,doubleh,intp);
}
具体观察者角色
publicclassHWeatherPanelimplementsObserver {
publicWeatherDatadata;
publicHWeatherPanel(WeatherData data) {
this.data= data;
data.addObserver(this);
}
@Override
publicvoidonWebtherChange(intx,doubleh,intp) {
//TODOAuto-generated method stub
System.out.println("更新湿度面板,最湿度是"+ h);
}
}
publicclassPWeatherPanelimplementsObserver {
publicWeatherDatadata;
publicPWeatherPanel(WeatherData data) {
this.data= data;
data.addObserver(this);
}
@Override
publicvoidonWebtherChange(intx,doubleh,intp) {
//TODOAuto-generated method stub
System.out.println("更新压力面板,最新压力是"+ p);
}
}
publicclassTWeatherPanelimplementsObserver {
publicWeatherDatadata;
publicTWeatherPanel(WeatherData data) {
this.data= data;
data.addObserver(this);
}
@Override
publicvoidonWebtherChange(intx,doubleh,intp) {
//TODOAuto-generated method stub
System.out.println("更新温度面板,最新温度是"+ x);
}
}
客户调用
publicclassMyTest {
/**
*@paramargs
*/
publicstaticvoidmain(String[] args) {
//TODOAuto-generated method stub
WeatherData data =newWeatherData();
HWeatherPanel hp =newHWeatherPanel(data);
PWeatherPanel pp =newPWeatherPanel(data);
TWeatherPanel tp =newTWeatherPanel(data);
data.measurementsChanged();
}
}
打印结果:
更新湿度面板,最湿度是0.53
更新压力面板,最新压力是30
更新温度面板,最新温度是0
Java中内置的观察者模式
到目前为止,我们已经从无到有地完成了观察者模式,但是,JavaAAPI有内置的观察者模式。java.util包(package)内包含最基本的Observer接口与Observable类,这和我们的Subject接口与Observer接口很相似。Observer接口与Observable类使用上更方便,因为许多功能都已经事先准备好了。
使用
接口Observer
一个可在观察者要得到observable对象更改通知时可实现Observer接口的类。
很显然 这个Observer接口就是对应我们自己实现中的Observer接口,(o,arg)方法就对应我们的onWebtherChange(intt,doubleh,intp)方法,这与我们的想法甚至名称都是一致的。
类Observable
方法摘要
void
(o)如果观察者与集合中已有的观察者不同,则向对象的观察者集中添加此观察者。
protected void
()指示对象不再改变,或者它已对其所有的观察者通知了最近的改变,所以hasChanged方法将返回false。
int
()返回Observable对象的观察者数目。
void
void
()清除观察者列表,使此对象不再有任何观察者。
boolean
()测试对象是否改变。
void
()如果hasChanged方法指示对象已改变,则通知其所有观察者,并调用clearChanged方法来指示此对象不再改变。
void
(arg)如果hasChanged方法指示对象已改变,则通知其所有观察者,并调用clearChanged方法来指示此对象不再改变。
protected void
()标记此Observable对象为已改变的对象;现在hasChanged方法将返回true。
由上表可以看出Observable类对应我们自己实现中的Subject接口,由于它是一个类,对于想要扩展的朋友就显得无能为力了,但是对于一般的观察者应用已经足够使了。
区别
我们不难发现,这个函数表中多个一个change的概念((),(),())它的引入是为了在更新观察者时,有更多的弹性,你可以更适当地通知观察者。比方说,如果没有setChanged()方法,我们的气象站测量是如此敏锐,以致于温度计读数每十分之一度就会更新,这会造成WeatherData对象持续不断地通知观察者,我们并不希望看到这样的事情发生。如果我们希望半度以上才更新,就可以在温度差距到达半度时,调用setChanged(),进行有效的更新。你也许不会经常用到此功能,但是把这样的功能准备好,当需要时马上就可以使用。总之,你需要调用setChanged(),以便通知开始运转。如果此功能在某些地方对你有帮助,你可能也需要clearChanged()方法,将changed状态设置回false。另外也有一个hasChanged()方法,告诉你changed标志的当前状态。
总结
优势
主题对象与观察者对象之间的松耦合,主题对象只知道Obserser接口,并不关谁实现了该接口以及怎么实现的,一旦有新类型观察者出现,我们只要把它注册到主题中就可以了,不用修改任何代码。
主要程序设计应用
这种设计模式广泛用于数据或状态的变化引起的界面刷新工作。
比如一个股票数据(价格)发生变化,可能会影响几个视图的更新(实时图,表图,甚至要入库操作。。。),我们就需要把这几个视图和操纵数据库的类作为观察者,注册到相应主题中去。