观察者模式
以报纸的订阅为例:
- 报社负责出版报纸。
- 向报社订购报纸后,当有新的报纸出版,报社就会给订购者送来。
- 当订购者不想再收到报纸时,取消订购即可。
报社 + 订购者 = 观察者模式。
观察者模式定义了对象之间的一对多依赖,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新。
主 题(报社) :发送通知给所有实现了观察者接口的对象。
观察者(订购者):实现观察者接口,并注册为观察者即可。
松耦合
松耦合是观察者模式的一个重要特点。改变主题或观察者,并不会影响另一方。
如果两个对象是松耦合的,它们可以交互,但不太清楚彼此的细节。只要两者遵守它们之间的接口,我们就可以自由的改变它们。
场景介绍
假设现在要建设一个气象观测站,主要分为三个部分:
- 气象站 —— 获取实际气象数据的物理装置
- 三个布告板 —— 显示当前的天气状况:目前状况、气象统计、天气预报
- WeatherData对象 —— 追踪气象站的数据,并更新布告板
UML设计图
代码示例
接口
主题 —— Subject
public interface Subject {
public void registerObserver(Observer o);
public void removeObserver(Observer o);
public void notifyObservers();
}
观察者 —— Observer
public interface Observer {
public void update(int value);
}
展示者 —— DisplayElement
public interface DisplayElement {
public void display();
}
类
主题
public class WeatherData implements Subject {
// 以List记录观察者
private List<Observer> observers;
private float temperature;
private float humidity;
private float pressure;
public WeatherData() {
observers = new ArrayList<Observer>();
}
// 注册观察者时,添加到list后即可
public void registerObserver(Observer o) {
observers.add(o);
}
// 当某个观察者取消订阅后,将其从list中删除即可
public void removeObserver(Observer o) {
observers.remove(o);
}
// 当数据变化时,遍历list,调用所有观察者的update接口进行数据更新
public void notifyObservers() {
for (Observer observer : observers) {
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 float getTemperature() {
return temperature;
}
public float getHumidity() {
return humidity;
}
public float getPressure() {
return pressure;
}
}
观察者
public class StatisticsDisplay implements Observer, DisplayElement {
private float maxTemp = 0.0f;
private float minTemp = 200;
private float tempSum= 0.0f;
private int numReadings;
private WeatherData weatherData;
public StatisticsDisplay(WeatherData weatherData) {
this.weatherData = weatherData;
weatherData.registerObserver(this);
}
public void update(float temp, float humidity, float pressure) {
tempSum += temp;
numReadings++;
if (temp > maxTemp) {
maxTemp = temp;
}
if (temp < minTemp) {
minTemp = temp;
}
display();
}
public void display() {
System.out.println("Avg/Max/Min temperature = " + (tempSum / numReadings)
+ "/" + maxTemp + "/" + minTemp);
}
}
测试程序
public class WeatherStationHeatIndex {
public static void main(String[] args) {
WeatherData weatherData = new WeatherData();
CurrentConditionsDisplay currentDisplay = new CurrentConditionsDisplay(weatherData);
StatisticsDisplay statisticsDisplay = new StatisticsDisplay(weatherData);
weatherData.setMeasurements(80, 65, 30.4f);
weatherData.setMeasurements(82, 70, 29.2f);
weatherData.setMeasurements(78, 90, 29.2f);
}
}
Java内置的观察者模式
Java中内置的观察者模式与上述例子类似,主要来源于两个包:
import java.util.Observable; import java.util.Observer;
特点
Java内置的观察者模式与上述例子最大的不同是在主题中,增加了setChanged()方法:
protected synchronized void setChanged() {
changed = true;
}
setChanged()方法用来标记状态已经改变的事实,让主题知道此时应该通知观察者。
如果没有setChanged()方法,气象站的测量非常敏锐,一旦温度变化就会立即更新,就会造成主题持续不断的通知观察者。但事实上我们可能希望温度变化超过半度才更新。setChanged()方法就是为此而生。
缺点
Observable是一个类!!!这导致Observable的复用潜力被限制了(因为Java不支持多重继承)。
应用举例
-
JDK 提供的观察者接口:Observable 类以及 Observer 接口。
- Spring中的ApplicationListener
- Guava 中的 EventBus