观察者模式
1. 定义:
观察者模式:定义了对象之间的一对多依赖,这样一来,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新
2.构成
主题(subject)+订阅者(observer)=观察者模式
主题对象:管理某些数据; 当主题内的数据改变,就会通知观察者
观察者:已经订阅(注册)主题以便在主题数据改变时能够收到更新
Subject:主题接口,对象使用此接口注册为观察者,或把自己从观察者中删除
ConcreteSubject:一个具体主题总是实现主题接口,除了注册和撤销方法之外,具体方法还实现了notifyObservers()方法,此方法用于在状态改变时更新所有当前观察者;具体主题也可能有设置和获取状态的方法
Observer:所有潜在的观察者都必须实现观察者接口,这个接口只有update()一个方法,当主题状态改变时它被调用
ConcreteObserver:具体的观察者可以是实现此接口的任意类。观察者必须注册具体主题,以便接受更新
3. 优点
观察者模式提供了一种对象设计,让主题和观察者之间
松耦合
4. 示例
气象监测应用
组成:
- 气象站
- WeatherData对象(追踪来自气象站的数据、更新布告板)
- 布告板
CurrentConditionDisplay:此布告板根据WeatherData对象显示当前观测值
StatisticsDisplay:此布告板根据最小、平均、最大的观测值,并显示它们
ForecastDisplay:此布告板根据气压计显示天气预报
public interface Subject {
// 注册观察者
void registerObserver(Observer o);
// 删除观察者
void removeObserver(Observer o);
// 主题状态改变时,通知所有的观察者
void notifyObservers();
}
public class WeatherData implements Subject {
private ArrayList<Observer> observers;
private float temperature;
private float humidity;
private float pressure;
public WeatherData() {
observers = new ArrayList<>();
}
public void registerObserver(Observer o) {
observers.add(o);
}
public void removeObserver(Observer o) {
int i = observers.indexOf(o);
if(i >= 0) {
observers.remove(i);
}
}
public void notifyObservers() {
for(Observer o : observers) {
o.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 interface Observer {
// 当气象观测值改变时,把状态传递给观察者
void update(float temp, float humidity, float pressure);
}
public interface DisplayElement {
// 布告板显示信息
void display();
}
public class CurrentConditionDisplay implements Observer, DisplayElement {
private float temperature;
private float humidity;
private Subject weatherData;
public CurrentConditionDisplay(Subject weatherData) {
this.weatherData = weatherData;
weatherData.registerObserver(this);
}
public void display() {
System.out.println("===============================");
System.out.println("CurrentConditionDisplay: ");
System.out.println("temperature: "+temperature);
System.out.println("humidity: "+humidity);
System.out.println("===============================");
}
public void update(float temp, float humidity, float pressure) {
this.temperature = temp;
this.humidity = humidity;
display();
}
}
5. Java内置的观察者模式
java.util包内包含基本的Observer和Observable类
主题扩展自Observable类,并继承到一些增加、删除、通知观察者的方法(以及其他方法)
送出通知:
- 先调用setChanged()方法,标记状态已经改变的事实
- 然后调用两种notifyObservers()方法中的一个:
notifyObservers()(拉式)
notifyObservers(Object arg) 当通知时,此版本可以传送任何的数据对象给每一个观察者(推式)
观察者接受通知
update(Observable o, Object arg)
o:主题本身
arg:传入notifyObservers()的数据对象;如果没有说明则为空
1) 使用推(push)的方式传递数据
在一次通知中一口气通知观察者所有数据
notifyObservers(Object arg)
2) 使用拉(pull)的方式传递数据
主题提供公开的getter方法让观察者“拉”走数据
notifyObservers()
代码示例:
public class WeatherData extends Observable {
private float temperature;
private float humidity;
private float pressure;
public WeatherData() {}
public void measurementChanged() {
setChanged();
notifyObservers();
}
public void setMeasurements(float temperature, float humidity, float pressure) {
this.temperature = temperature;
this.humidity = humidity;
this.pressure = pressure;
measurementChanged();
}
public float getTemperature() {
return temperature;
}
public float getHumidity() {
return humidity;
}
public float getPressure() {
return pressure;
}
}
public class CurrentConditionDisplay implements Observer, DisplayElement {
Observable observable;
private float temperature;
private float humidity;
public CurrentConditionDisplay(Observable observable) {
this.observable = observable;
observable.addObserver(this);
}
@Override
public void update(Observable o, Object arg) {
if(arg instanceof WeatherData) {
WeatherData weatherData = (WeatherData)arg;
this.temperature = weatherData.getTemperature();
this.humidity = weatherData.getHumidity();
display();
}
}
@Override
public void display() {
System.out.println("===============================");
System.out.println("CurrentConditionDisplay: ");
System.out.println("temperature: "+temperature);
System.out.println("humidity: "+humidity);
System.out.println("===============================");
}
}
不要依赖于观察者被通知的次序
java.util.Observable实现了它的notifyObservers()方法,这导致了通知观察者的次序并不同于我们先前的次序
Java内置Observable的缺点
- Observable是一个类,而不是一个接口
Java不支持多继承,限制了Observable的复用潜力 - Observable将关键的方法保护起来
setChanged()方法被保护起来(protected),除非继承Observable,否则无法创建Observable实例并组合到自己的对象中
违反了设计原则:“多用组合,少用继承”
6. JDK中其他的观察者模式
Swing中的JButton
JavaBeans
RMI