Referenced by Head First Design Pattern
观察者模式提供了一种对象设计,让主题和观察者之间松耦合
对于实现气象站的功能,我们有一个WeatherData对象负责追踪目前的天气状况,我们的目的是希望建立几个布告板,当天气变化时候更新它们。
对于WeatherData对象的说明:
这个对象提供了get方法来读取天气状况,同时气象测试时,会调用measurementsChanged()方法
需要注意的是,我们有多个布告板需要更新,那么应该怎么做呢?
最直接的做法,在measurementsChanged()中直接更新布告板:
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);//分别更新每个布告板
}
但是我们犯了很明显的错误:
1.针对具体实现编程,而非针对接口。
2.对于每个新的布告板,我们都得修改代码。
3.我们无法再运行时动态的添加(或删除)布告板
4.我们尚未封装改变的部分。
那么,什么是观察者模式呢
观察者模式:定义了对象之间的一对多依赖,这样一来,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新。
设计原则:为了交互对象之间的松耦合设计而努力。
松耦合的设计之所以能让对象建立有弹性的OO系统,能够应对变化,是因为对象之间的互相依赖降到了最低。
观察者模式类图:
主题接口:
public interface Subject {
public void registerObserver(Observer o);//添加观察者
public void removeObserver(Observer o);//删除观察者
public void notifyObservers();//当主题改变时,调用这个方法来通知观察者
}
观察者接口:
public interface Observer {
public void update(float tmp, float humidity, float pressure);//观察者需要实现这个方法来接收通知
}
WeatherData类实现具体的主题:
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();
}
}
定义一个布告板对象来实现具体的观察者:
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 tmp, float humidity, float pressure) {
this.temperature = tmp;
this.humidity = humidity;
display();
}
@Override
public void display() {
System.out.println("Current conditions: "+temperature+"F degrees and "+humidity+"% humidity");
}
}
测试时,就可以使用:
WeatherData weatherData = new WeatherData();
CurrentConditionsDisplay currentDisplay = new CurrentConditionsDisplay(weatherData);//同时构造函数中将布告板对象添加到观察者列表中去
//还可以建立其他的公告板
weatherData.setMeasurements(80, 65, 30.4f);
weatherData.setMeasurements(82, 70, 29.2f);
weatherData.setMeasurements(78, 90, 29.2f);
在JDK中,有许多地方用到了观察者模式,比如button的事件监听等。
JButton button = new JButton("Should I do it?");
button.addActionListener(new AngelListener());
button.addActionListener(new DevilListener());//建立了2个
class AngelListener implements ActionListener{
@Override
public void actionPerformed(ActionEvent e){}//当按下按钮时被调用
}