认识观察者模式
我们看看报纸和杂志社的订阅是怎么回事:
- 报社的业务就是出版报纸。
- 向某家报社订阅报纸,只要他们有新报纸出版,就会给你送来。只要你是他们的订户,你就会一直收到新报纸。
- 当你不想再看报纸的时候,取消订阅,他们就不会再送新报纸来。
- 只要报社还在运营,就会一直有人(或单位)向他们订阅报纸或取消订阅报纸。

如果你了解了报纸的订阅是怎么回事,其实就知道观察者模式是怎么回事,只是名称不太一样:出版社改称为“主题(Subject)”,订阅者改称为“观察者(Observer)”。
定义
观察者模式定义了对象之间的一对多以来,这样一来,当一个对象改变状态时,它的所有依赖着都会收到通知并自动更新。
结构图

具体的解释看下图:
实现
假设现在要气象监测的应用,此系统中由三个部分,分别是气象站(获取实际气象数据的物理装置),WeatherData对象(追踪来自气象站的数据,并更新布告板),布告板(显示目前天气状况给用户看)。
WeatherData对象知道如何跟物理气象站联系,以取得更新的数据。WeatherData对象会随即更新三个布告板的显示:目前状况(温度、湿度、气压)、对象统计和天气预报。我们的工作就是建立一个应用,利用WeatherData对象取得数据,并更新三个布告板:目前状况、气象统计和天气预报。
因此根据上面的结构图开始设计这个气象站应用:
实现主题
先建立一个主题接口,提供WeatherData实现的基础:
1 | public interface Subject { |
然后创建WeatherData类,实现Subject接口,说明它是主题的角色:
1 | public class WeatherData implements Subject { |
这样告示板就可以通过WeatherData这个对象进行注册和删除,而当气象站的数据更新时,就可以调用WeatherData的setMeasurements()方法进行数据更改,而且会自动通知给所有订阅的告示板。
观察者接口
创建一个观察者接口,作为所有观察者的基础,其中只有一个方法update(),用于被通知。
1 | public interface Observer { |
告示板接口
此处为告示板实现基础的接口:DisplayElement,用于标识所实现的类为告示板:
1 | public interface DisplayElement { |
观察者实现
下面建立三个告示板,分别用来展示目前状况(温度、湿度、气压)、对象统计和天气预报。因此所有的告示板实现类都需要实现Observer接口,以标识它是一个观察者,而且可以接收到通知;并且实现DisplayElement接口,标识它是一个告示板。
目前状况告示板:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25public class CurrentConditionDisplay implements Observer,DisplayElement{
private float temperature;
private float humidity;
private float pressure;
private Subject weatherData;
public CurrentConditionDisplay(Subject weatherData) {
this.weatherData = weatherData;
weatherData.registerObserver(this);
}
public void display() {
System.out.println("CurrentConditionDisplay:温度:" + temperature + "度,湿度:" + humidity + "%,压强:" + pressure);
}
public void update(float temperature, float humidity, float pressure) {
this.temperature = temperature;
this.humidity = humidity;
this.pressure =pressure;
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
25public class StatisticsDisplay implements Observer,DisplayElement {
private float temperature;
private float humidity;
private float pressure;
private Subject weatherData;
public StatisticsDisplay(Subject weatherData) {
this.weatherData = weatherData;
weatherData.registerObserver(this);
}
public void display() {
System.out.println("StatisticsDisplay:温度:" + temperature + "度,湿度:" + humidity + "%,压强:" + pressure);
}
public void update(float temperature, float humidity, float pressure) {
this.temperature = temperature;
this.humidity = humidity;
this.pressure =pressure;
display();
}
}天气预报告示板:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24public class ForecastDisplay implements Observer, DisplayElement{
private float temperature;
private float humidity;
private float pressure;
private Subject weatherData;
public ForecastDisplay(Subject weatherData) {
this.weatherData = weatherData;
weatherData.registerObserver(this);
}
public void display() {
System.out.println("ForecastDisplay:温度:" + temperature + "度,湿度:" + humidity + "%,压强:" + pressure);
}
public void update(float temperature, float humidity, float pressure) {
this.temperature = temperature;
this.humidity = humidity;
this.pressure =pressure;
display();
}
}
查看结果
下面进行测试:
1 | public class Main { |
输出的结果如下:
1 | CurrentConditionDisplay:温度:0.0度,湿度:0.0%,压强:0.0 |
整体结构

总结
观察者模式定义对象之间的一对多依赖,当一个对象状态改变时,它的所有依赖都会收到通知并且自动更新状态。
主题(Subject)是被观察的对象,而其所有依赖者(Observer)称为观察者。
- 观察者模式定义了对象之间一对多的关系。
- 主题(也就是可观察者)用一个共同的接口来更新观察者。
- 观察者和可观察者之间用松耦合方式结合,可观察者不知道观察者的细节,只知道观察者实现了观察者接口。
- 使用此模式时,你可从被观察者处推(push)或拉(pull)数据,然而,推的方式被认为更正确。
- 有多个观察者时,不可以依赖特定的通知次序。
- Java有多种观察者模式的实现,包括了通用的java.util.Observable。