本文对《Head First 设计模式》中的观察者模式进行了概括和总结
观察者模式——在对象之间定义一对多的依赖,这样一来,当一个对象改变状态,依赖他的对象都会收到通知,并自动更新。
问题描述
要求公司建立一个 气象数据站 (WeatherData对象),使之能从 气象观测站 (WeatherStation对象)获取气象数据(温度temperature,湿度humidity,气压值pressure)。
并要求公司开发一个应用,使其包含三个天气显示板:目前状况、气象统计、天气预测,这三个对象都获取了来自WeatherData的三个气象数据,但是对这些数据的处理方式不一样。要求显示板的显示数据根据WeatherData传过来的气象数据实时变化。
下图就是将气象数据传给了“目前状况”这个展示板。
下面进入正题。
观察者模式的思想
主题(Subject)+观察者(Observer)=观察者模式
下图是Subject和Observer之间的关系
观察者模式提供了一种对象设计,让主题和观察者之间松耦合。
关于观察者的一切,主题只知道观察者实现了某个接口(Observer接口),主题只需要知道这个观察者 注册与否 (是否需要定期向它提供消息),如果注册了,就定期发送消息给他。主题不需要知道观察者本身具体是谁、做了什么以及其他细节。
当新的观察者注册时,不用改主题代码,只需将通知的消息给这个新来的观察者发一份就行了,观察者不在乎别的,它只会将相同消息发送给所有实现了Observer接口的具体观察者对象,它一视同仁。
以下是本例利用观察者模式的结构图
下面给出本例WeatherData类的实现代码
class WeatherData implements Subject {
private float temperature;
private float humidity;
private float pressure;
private ArrayList observers;
public WeatherData(){
List<Observer> observers = new ArrayList<Observer>();
}
@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(o);
}
}
@Override
public void notifyObservers() { //通知各个观察者
for(int i = 0;i<observers.size();i++){ //遍历观察者List,挨个调用每个观察者的update()方法,实现通知
Observer observer = (Observer) observers.get(i);
observer.update(temperature,humidity,pressure);
}
}
public void measurementsChanged(){
notifyObservers();
}
public void setMeasurements(float temp,float humidity,float pressure){
this.temperature = temp;
this.humidity = humidity;
this.pressure = pressure;
measurementsChanged();
WeatherStation.times++;
}
}
以下是其中一个具体观察者的代码实现:
class CurrentConditionDisplay implements Observer{
private float temperature;
private float humidity;
private Subject weatherData;
public CurrentConditionDisplay(Subject weatherData){ //通过观察者的构造器将weatherData传进来,并将该观察者注册
this.weatherData = weatherData;
weatherData.registerObserver(this);
//weatherData.removeObserver(this);
}
public void display() {
System.out.println("current condition: "+temperature+"F degrees and"+humidity+"% humidity");
}
@Override
public void update(float temp, float humidity, float pressure) { //该方法由weatherData的notifyObserver()方法触发
this.temperature = temp;
this.humidity = humidity;
display();
}
}
总结
该设计模式的关键在于:如何将具体主题(WeatherData)和具体观察者(Concret Observer)相互联系起来
解析:
1、观察者们利用weatherData对象去注册和注销
registerObserver()方法和removeObserver()方法都在weatherData对象中,因此具体观察者的注册必须通过weatherData对象来完成,但是是否注册还是由具体观察者它自己决定,所以得将weatherData对象传到具体观察者内部,显然直接将weatherData对象传入具体观察者的构造器最直观,因为当new出具体观察者对象的时刻就能决定是否注册为观察者了。
2、weatherData利用观察者们的对象才能调用它们的update()方法
由于有多个观察者,因此可用一个Observer类型的List来存放注册了的Observer,一旦某个Observer注册成功,该观察者的对象就传到了List中,因此weatherData可以访问所有注册过的观察者。这样的目的在这里很明确,一旦新数据到来,weatherData对象就调用notifyObserver()方法,进而利用List中的所有观察者对象去调用它们各自的update()方法。