
设计模式之观察者模式
让你的对象知悉现况
有一个模式可以帮你的对象知悉现况,不会错过该对象感兴趣的事。对象甚至在运行时可决定是否需要继续被通知。观察者模式是JDK中使用最多的模式之一,非常有用。我们会一并介绍一对多关系,以及松耦合。有了观察者,你将会消息灵通。
让我们来模拟一个气象监测应用
此系统中的三个部分是气象站(获取实际气象数据的物理装置)、WeatherData对象(追踪来自气象站的数据)和公告板(显示目前天气状况给用户看)。
我们的工作就是建立一个应用,利用WeatherData对象取得数据,并更新三个公告板:目前状况、气象统计和天气预报。
瞧瞧WeatherData类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| public class WeatherData {
private float getTemperature() { return 0; }
private float getHumidity() { return 0; }
private float getPressure() { return 0; }
private void measurementsChanged() { float temperature = getTemperature(); float humidity = getHumidity(); float pressure = getPressure();
currentConditionsDisplay.update(temperature, humidity, pressure); statisticsDisplay.update(temperature, humidity, pressure); forecastDisplay.update(temperature, humidity, pressure); } }
|
定义观察者模式
观察者模式定义了对象之间的一对多依赖,这样一来,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新。
定义观察者模式类图

设计原则
为了交互对象之间的松耦合设计而努力。
使用观察者来改造气象监测应用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
|
public interface Subject {
void registerObserver(Observer observer);
void removeObserver(Observer observer);
void notifyObservers(); }
|
1 2 3 4 5 6 7 8 9
| public interface Observer {
void update(float temperature, float humidity, float pressure); }
|
1 2 3 4 5 6
| public interface DisplayElement {
void display(); }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
| public class WeatherData implements Subject { private final ArrayList<Observer> observers; private float temperature; private float humidity; private float pressure;
public WeatherData() { this.observers = new ArrayList<>(); }
public void setMeasurements(float temperature, float humidity, float pressure) { this.temperature = temperature; this.humidity = humidity; this.pressure = pressure; notifyObservers(); }
@Override public void registerObserver(Observer observer) { observers.add(observer); }
@Override public void removeObserver(Observer observer) { int i = observers.indexOf(observer); if (i > 0) { observers.remove(i); } }
@Override public void notifyObservers() { for (Observer observer : observers) { observer.update(temperature, humidity, pressure); } } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67
|
public class CurrentConditionsDisplay implements Observer, DisplayElement { private float temperature; private float humidity;
@Override public void display() { System.out.printf("Current Conditions: %s F degrees and %s %% humidity.\n", temperature, humidity); }
@Override public void update(float temperature, float humidity, float pressure) { this.temperature = temperature; this.humidity = humidity; display(); } }
public class StatisticsDisplay implements Observer, DisplayElement {
private float temperature; private float humidity;
@Override public void display() { System.out.printf("Current Conditions: %s F degrees and %s %% humidity.\n", temperature, humidity); }
@Override public void update(float temperature, float humidity, float pressure) { this.temperature = temperature; this.humidity = humidity; display(); } }
public class ForecastDisplay implements Observer, DisplayElement {
private float temperature; private float humidity;
@Override public void display() { System.out.printf("Current Conditions: %s F degrees and %s %% humidity.\n", temperature, humidity); }
@Override public void update(float temperature, float humidity, float pressure) { this.temperature = temperature; this.humidity = humidity; display(); } }
|
启动气象站
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| public class WeatherStation { public static void main(String[] args) { WeatherData weatherData = new WeatherData();
CurrentConditionsDisplay currentDisplay = new CurrentConditionsDisplay(); StatisticsDisplay statisticsDisplay = new StatisticsDisplay(); ForecastDisplay forecastDisplay = new ForecastDisplay();
weatherData.registerObserver(currentDisplay); weatherData.registerObserver(statisticsDisplay); weatherData.registerObserver(forecastDisplay);
weatherData.setMeasurements(80, 56, 29.4f); weatherData.setMeasurements(82, 70, 39.2f); weatherData.setMeasurements(75, 90, 20.1f); } }
|
使用JDK 内置观察者模式来改造应用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| ```java /** * 可观察者主题对象 * @author Mr.zxb * @date 2020-08-11 20:11:46 */ public class WeatherDataObservable extends Observable { private float temperature; private float humidity; private float pressure; /** * 一旦气象测量更新,此方法就会被调用 */ public void setMeasurements(float temperature, float humidity, float pressure) { // 获取温度、湿度、气压 this.temperature = temperature; this.humidity = humidity; this.pressure = pressure; // 指示状态已经改变 super.setChanged(); // 通知观察者 super.notifyObservers(); }
}
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| package com.design.observer;
import java.util.Observable; import java.util.Observer;
public class CurrentConditionsDisplayObserver implements Observer, DisplayElement { private float temperature; private float humidity;
@Override public void display() { System.out.printf("Current Conditions: %s F degrees and %s %% humidity.\n", temperature, humidity); }
@Override public void update(Observable o, Object arg) { if (o instanceof WeatherDataObservable) { WeatherDataObservable weatherDataObservable = (WeatherDataObservable) o; this.temperature = weatherDataObservable.getTemperature(); this.humidity = weatherDataObservable.getHumidity();
this.display(); } } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
|
public class WeatherStationObservable { public static void main(String[] args) { WeatherDataObservable weatherData = new WeatherDataObservable();
CurrentConditionsDisplayObserver currentDisplay = new CurrentConditionsDisplayObserver();
weatherData.addObserver(currentDisplay);
weatherData.setMeasurements(80, 56, 29.4f); weatherData.setMeasurements(82, 70, 39.2f); } }
|
总结
观察者模式定义了对象之间一对多的关系
主题(也就是可观察者)用一个共同的接口来更新观察者
观察者和可观察者之间用松耦合方式结合,可观察者不知道观察者的细节,只知道观察者实现了观察者接口
使用此模式时,你可从被观察者处推(push)和拉(pull)数据
有多个观察者时,不可以依赖特定的通知次序
Java有多种观察者模式的实现,包括了通用的 java java.util.Observable
要注意java java.util.Observable
实现上带来的一些问题
如果有必要,可以实现自己的Observable