给出问题事例:有一个WeatherData对象可以给我们提供实时的天气情况(温度、湿度、气压),我们要做的就是一个应用,这个应用目前有三个布告板,分别显示目前的状况、气象统计以及简单的预报。这三个布告板可以根据WeatherData的实时值进行实时更新。这个系统必须可扩展,让其他开发人员建立定制的布告板,用户可以随心所欲的删除或者添加布告板。
下面给出了WeatherData的类结构:
WeatherData |
//以下给出了三个方法,分别获得温度、湿度、气压 getTemperature(); getHumidity(); getPressure(); //一旦气象更新,这个方法会被调用 measurementsChanged(); |
我们貌似很快就能找到解决办法:
public class WeatherData{
//实例变量声明。
public void measurementsChanged(){
float temp=getTemperature();
float humidity=getHumidity();
float pressure=getPressure();
currentConditionDislplay.update(temp,humidity,pressure);
statisticsDisplay.update(temp,humidity,pressure);
forecastDisplay.update(temp,humidity,pressure);
}
}
仔细分析下,我们的实现有什么不对吗?
对,就是那些蓝色部分,想想我们讲的策略模式,这就是针对实现编程,这样会导致我们以后增加或者删除布告板时必须修改程序。
好的,这时候我们主角就应该上场了,那就是观察者模式。我们首先给出观察者模式的定义:观察者模式定义了对象之间的一对多依赖,这样一来,当一个对象的状态改变时,它的所有依赖者都能收到通知并立即自动更新。
我们将观察者模式中的两种对象分别命名为主题对象和观察者对象。主题对象就是我们定义里的“一”,观察者对象就是我们定义里的“多”。多主题对象的状态改变时,主题对象的所有依赖者(即观察者)都能收到通知并自动更新。有没有想起来Android里的广播?下面我们举个小例子来说明主题和观察者。假如一个主题对象的观察者有猫对象,狗对象,老虎对象。而老鼠对象不依赖主题对象。当主题对象改变时,猫对象、狗对象以及老虎对象会收到通知,而老鼠对象不会收到通知。如果老鼠对象想收到通知,它必须在主题对象里进行注册。如果猫对象不想再收到通知,它可以告诉主题对象,主题对象知道猫的请求之后,把它从观察者中删除,则猫对象今后就不会再收到通知。
观察者提供了一种对象设计,让主题和观察者之间松耦合。
所以我们给出了第四个设计原则:
为了交互对象之间的松耦合设计而努力。
OK,下面我们开始使用观察者模式重新设计我们的实现。让我们先从接口开始。
public interface Subject{
//以下两个方法都需要一个观察者作为参数,该观察者是用来注册或者删除的。
public void registerObserver(Observer o);
public void removerObserver(Observer o);
//当主题状态改变时,这个方法会被调用,以通知所有的观察者。
public void notigyObservers();
}
public interface Observer{
/ /所有的观察者都要实现这个方法,以实现观察者接口。当气象观测值改变时,主题会传递这些状态值。
public void update(float temp,float humidity,float pressure);
}
public void displayElement{
//当布告板需要显示时,调用此方法。
public void display();
}
下面我们使用观察者模式来实现WeatherData。
public class WeatherData implements Subject{//WeatherData现在实现了Subject接口。
private ArrayList<Observer> observers; //用来记录观察者。
private float temperature;
private float humidity;
private float pressure;
public WeatherData(){
observers=newArrayList()<Observer>;
}
public void registerObserver(Observer o){
observers.add(o);
}
public void removerObserver(Observer o){
inti=observers.indexOf(o);
if(i>=0){
observers.remove(i);
}
}
public void notigyObservers(){
for(Observero:observers){
o.update(temperature,humidity,pressure);
}
}
//从气象站得到更新的值时,通知观察者。
public void measurementsChanged(){
notigyObservers();
}
public void setMeasurements(float temperature,float humidity,floatpressure){
this.temperature=tempreture;
this.humidity=humidity;
this.pressure=pressure;
measurementsChanged();
}
}
接下来,我们来建立我们的布告板。我这里只实现当前状态布告板,其他的两个大家可以自己动手实现。
public class CurrentConditionsDisplay implements Observer,DisplayElement{
private float temperature;
private float humidity;
private float pressure;
private Subject weatherData;
public CurrentConditionDisplay(Subject weatherData){
this.weatherData=weatherData;
//注册自己为观察者
weatherData.registerObserver(this);
}
public void updata(float temperature,float humidity,float pressure){
this.temperature=temperature;
this.humidity=humidity;
this.pressure=pressure;
display();
}
public void display(){
System.out.println(“Currentconditions:”+temperature+”F degress”+humidity+”%humidity”+pressure+”% pressure);
}
}
写一个测试程序。
public class WeatherStation{
public static void main(String[] args){
WeatherData weatherData=new WeatherData();
CurrentConditionsDisplay currentConditionsDisplay=new CurrentConditionsDisplay(weatherData);
StatisticsDisplay statisticsDisplay=new StatisticsDisplay(weatherData);
ForecastDisplay forecastDisplay=new ForecastDisplay(weatherData);
weatherData.setMeasurements(30,60,30.4f);
weatherData.setMeasurements(40,50,50.4f);
weatherData.setMeasurements(35,40,20.4f);
}
}
程序的输出大家应该都很清楚了。