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