您的位置:首页 > 其它

设计模式学习之二观察者模式(Observer)——参与气象观测站的设计

2011-05-05 22:35 651 查看
业务需求:希望建立下一代Internet气象观测站!该气象站必须建立在我们专利申请中的WeatherData对象上,由WeatherData对象负责追踪目前的天气状况(温度、湿度、气压)。要求:建立一个应用,有三种布告板,分别显示目前的状况,气象统计及简单的预报。当WeatherObject对象获得最新的测量数据时,三种布告板必须实时更新。而且,这是一个可以扩展的气象站,Weather-O-Rama气象站希望能有一组API,好让其他开发人员可以写出自己的气象布告板,并插入此应用中。 接下来看WeatherDate类


先看一个错误的示范
public class WeatherData {
//实例变量声明

public void measurementsChanged() {

float temp = getTemperature();
float humidity = getHumidity();
float pressure = getPressure();

currentConditionsDisplay.update(temp, humidity, pressure);
statisticsDisplay.update(temp, humidity, pressure);
forecastDisplay.update(temp, humidity, pressure);
}

//这里是其他的WeatherData方法
}

我们的实现有什么不对呢,仔细想想oo的设计原则就知道了

currentConditionsDisplay.update(temp, humidity, pressure);

statisticsDisplay.update(temp, humidity, pressure);

forecastDisplay.update(temp, humidity, pressure);

这里针对了具体实现编程,会使我们在增加或删除布告板时必须修改程序,而且布告板的方法都是update,至少这里看起来应该像是一个统一的接口。改变的地方需要封装滴。。。。

那么先让我们认识一下观察者模式吧,报纸订阅的方式估计大家都能想明白,如果你明白报纸订阅是怎么回事,那么恭喜你,你已经明白观察者模式是怎么回事了。 出版者+订阅者=观察者模式,把名称改一下吧,改的专业一点,出版者改成 主题(Subject), 订阅之改成 观察者(Observer),改后 主题(Subject)+ 观察者(Observer)=观察者模式(Observer),那么根据报纸的订阅方式可以给观察者模式下定义了:观察者模式定义了对象之间的一对多依赖,这样一来,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新。稍后你会看到实现观察者模式的方法不止一种,但是以包含Subject和Observer接口的类设计的做法最常见。下面是观察者模式的类图,直接截的图:



观察者模式提供了一种对象设计,让主题和观察者之间松耦合(解耦合),我们可以独立的复用主题或观察者,因为二者并非紧耦合,只要他们之间的接口仍被遵守,我们就可以自由的改变他们。由此,我们又碰到一个设计原则:为了交互对象之间的松耦合(解耦合)设计而努力。OK 看过观察者模式了,那么开始设计气象站吧,设计图如下:



根据上面的类图,来实现气象站吧,先从建立接口开始,代码如下:

public interface Subject {
public void registerObserver(Observer o);
public void removeObserver(Observer o);

//当主题变化时这个方法会被调用,通知所有的观察者
public void notifyObservers();
}
public interface Observer {
public void update(float temperature, float humidity, float pressure);
}
public interface DisplayElement {

//当布告板需要显示时调用此方法
public void display();
}


继续在WeatherData中实现主题接口

import java.util.ArrayList;
public class WeatherData implements Subject {
private ArrayList observers;
private float temperature;
private float humidity;
private float pressure;

public WeatherData() {
observers = new ArrayList();
}

@Override
public void registerObserver(Observer o) {
observers.add(o);
}
@Override
public void removeObserver(Observer o) {
int i = observers.indexOf(o);
if(i>=0) {
observers.remove(i);
}
}

@Override
public void notifyObservers() {
for(int i=0;i<observers.size();i++) {
Observer observer = (Observer)observers.get(i);
observer.update(temperature, humidity, pressure);
}

}
public void measurementsChanged() {
notifyObservers();
}

public void setMeasurements(float temperature, float humidity, float pressure) {
this.temperature = temperature;
this.humidity = humidity;
this.pressure = pressure;
measurementsChanged();
}

//这里是其他的WeatherData方法
}


接下来建立布告板:

public class CurrentConditionsDisplay implements Observer, DisplayElement {
private float temperature;
private float humidity;
private Subject weatherData;

public CurrentConditionsDisplay(Subject weatherData) {
this.weatherData = weatherData;
weatherData.registerObserver(this);
}

@Override
public void update(float temperature, float humidity, float pressure) {
this.temperature = temperature;
this.humidity = humidity;
display();
}
@Override
public void display() {
System.out.println("Current conditions: " + temperature + "F degrees and " + humidity + "%humidity");
}
}


最后可以建立一个测试程序来看我们的劳动成果了:

public class WeatherStation {
public static void main(String[] args) {
WeatherData weatherData = new WeatherData();
CurrentConditionsDisplay currentDisplay = new CurrentConditionsDisplay(weatherData);

//StatisticsDisplay statisticsDisplay = new StatisticsDisplay(weatherData);
//ForecastDisplay forecastDisplay = new ForecastDisplay(weatherData);

weatherData.setMeasurements(80, 65, 30.4f);
weatherData.setMeasurements(82, 70, 29.2f);
weatherData.setMeasurements(78, 90, 29.2f);
}
}


到此,气象站算初步完成了。但是 Java API 有内置的观察者模式。java.util包内包含最基本的Observer接口与Observable类,这和我们的Subject接口与Observer接口很类似,Observer接口与Observable类使用起来更方便,因为许多功能都已经事先准备好了,上面我们自己实现的只是使用推(push)传送数据,但在内置的模式中可以使用推(push)或拉(pull)的方式传送数据。使用内置的观察者模式重新设计气象站:



我们再做一次气象站,但这次用拉(pull)来传送数据。首先,把WeatherDate改成使用java.util.Observable:

import java.util.Observable;
public class WeatherData extends Observable {
private float temperature;
private float humidity;
private float pressure;

public WeatherData() {
}

public void measurementsChanged() {

//在调用notifyObservers()之前,要先调用setChanged()来指示状态已经改变
setChanged();
notifyObservers();
}

public void setMeasurements(float temperature, float humidity, float pressure) {
this.temperature = temperature;
this.humidity = humidity;
this.pressure = pressure;
measurementsChanged();
}
public float getTemperature() {
return temperature;
}
public float getHumidity() {
return humidity;
}
public float getPressure() {
return pressure;
}

}


现在也让我们重做CurrentConditionsDisplay:

import java.util.Observable;
import java.util.Observer;
public class CurrentConditionsDisplay implements Observer, DisplayElement {
Observable observable;
private float temperature;
private float humidity;

public CurrentConditionsDisplay(Observable observable) {
this.observable = observable;
observable.addObserver(this);
}
@Override
public void update(Observable obs, Object arg) {
if(obs instanceof WeatherData) {
WeatherData weatherData = (WeatherData)obs;
this.temperature = weatherData.getTemperature();
this.humidity = weatherData.getHumidity();
display();
}

}
@Override
public void display() {
System.out.println("Current conditions: " + temperature + "F degrees and " + humidity + "%humidity");

}

}


运行WeatherStation.java再次进行测试。。。。不知你注意到了吗,可观察者是一个“类”而不是一个“接口“,违反了针对接口编程,而非针对实现编程。如果你看看Observable的API,你会发现setChanged()方法被保护起来了(被定义成了protected),这意味着:除非你继承自Observable,否则无法创建Observable实例并组合到你自己的对象中来。这个设计违反了:多用组合,少用继承。但是当你熟悉观察者模式之后这一切都不是问题,你可以自己实现的,选择权在于你。其实,并非只有在java.util中才能找到观察者模式,其实在JavaBeans和Swing中,也都实现了观察者模式。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: