观察者模式是一种行为型设计模式,其核心思想是定义 “一对多” 的依赖关系:当一个对象(称为 “主题 / 被观察者”)的状态发生变化时,所有依赖于它的对象(称为 “观察者”)会自动收到通知并进行更新。

该模式的本质是 “解耦主题与观察者” —— 主题无需知道具体有哪些观察者,只需维护一个观察者列表;观察者也无需了解主题的内部逻辑,只需在收到通知时执行自身的更新逻辑,符合 “开闭原则”。

image.png

下面我将用 Java 代码实现观察者模式,以气象站系统为例,展示当气象数据变化时,多个显示器如何自动更新显示内容。

首先,我们要定义一个抽象主题接口,它的作用是注册、移除和通知观察者。

1
2
3
4
5
6
7
8
9
10
11
12
// 抽象主题接口
public interface Subject {
// 注册观察者
void registerObserver(Observer observer);

// 移除观察者
void removeObserver(Observer observer);

// 通知所有观察者
void notifyObservers();
}

具体的主题,也就是被观察者要实现Subject接口。它会维护气象数据和观察者列表。当数据更新时,会通知所有的注册者。

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
import java.util.ArrayList;
import java.util.List;

// 具体主题:气象数据中心
public class WeatherData implements Subject {
private double temperature; // 温度
private double humidity; // 湿度
private List<Observer> observers; // 观察者列表

public WeatherData() {
observers = new ArrayList<>();
}

@Override
public void registerObserver(Observer observer) {
observers.add(observer);
}

@Override
public void removeObserver(Observer observer) {
observers.remove(observer);
}

@Override
public void notifyObservers() {
// 通知所有观察者
for (Observer observer : observers) {
observer.update();
}
}

// 更新气象数据并通知观察者
public void setWeatherData(double temperature, double humidity) {
this.temperature = temperature;
this.humidity = humidity;
// 数据更新后通知所有观察者
notifyObservers();
}

// 提供getter方法,供观察者获取最新数据(拉模式)
public double getTemperature() {
return temperature;
}

public double getHumidity() {
return humidity;
}
}

我们还需要定义观察者的抽象接口,所有的具体观察者都要继承它,这个接口定义了更新丰富,也就是当被观察者对象发送变化时通过这个来通知观察者

1
2
3
4
5
6
// 抽象观察者接口
public interface Observer {
// 当主题状态变化时,调用此方法更新
void update();
}

有了接口抽象,接下来定义具体的观察者,这里我们定义了温度和湿度的观察者,当气候发送变化时他们会收到通知并获取气候数据来显示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 具体观察者:湿度显示器
public class HumidityDisplay implements Observer {
private WeatherData weatherData; // 持有主题引用,用于获取数据

// 构造器:注册到主题
public HumidityDisplay(WeatherData weatherData) {
this.weatherData = weatherData;
weatherData.registerObserver(this);
}

@Override
public void update() {
// 从主题拉取最新湿度数据
double humidity = weatherData.getHumidity();
display(humidity);
}

// 显示湿度信息
private void display(double humidity) {
System.out.println("湿度显示器: 当前湿度为 " + humidity + " %");
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 具体观察者:温度显示器
public class TemperatureDisplay implements Observer {
private WeatherData weatherData; // 持有主题引用,用于获取数据

// 构造器:注册到主题
public TemperatureDisplay(WeatherData weatherData) {
this.weatherData = weatherData;
weatherData.registerObserver(this);
}

@Override
public void update() {
// 从主题拉取最新温度数据
double temperature = weatherData.getTemperature();
display(temperature);
}

// 显示温度信息
private void display(double temperature) {
System.out.println("温度显示器: 当前温度为 " + temperature + " °C");
}
}

最后通过客户端代码来演示它

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 客户端测试类
public class Client {
public static void main(String[] args) {
// 创建主题(被观察者):气象数据中心
WeatherData weatherData = new WeatherData();

// 创建观察者:温度显示器和湿度显示器
// 观察者在创建时会自动注册到主题
new TemperatureDisplay(weatherData);
new HumidityDisplay(weatherData);

// 模拟气象数据更新
System.out.println("--- 第一次更新气象数据 ---");
weatherData.setWeatherData(25.5, 60.0);

System.out.println("\n--- 第二次更新气象数据 ---");
weatherData.setWeatherData(26.3, 58.5);

System.out.println("\n--- 第三次更新气象数据 ---");
weatherData.setWeatherData(24.8, 62.3);
}
}

从上面的代码可以看出:

  1. 客户端创建具体主题(WeatherData)实例
  2. 创建具体观察者(TemperatureDisplay、HumidityDisplay)时,观察者会自动注册到主题
  3. 当主题的状态(气象数据)发生变化时:
    • 调用主题的 setWeatherData () 方法更新数据
    • 主题内部调用 notifyObservers () 方法通知所有观察者
    • 每个观察者的 update () 方法被调用
    • 观察者从主题拉取最新数据并更新显示

这种设计模式的优势在于:

  • 主题和观察者之间实现了解耦,彼此不需要知道对方的具体实现
  • 可以方便地添加新的观察者,无需修改主题代码
  • 当主题状态变化时,所有相关观察者会自动收到通知并更新
  • 符合开闭原则,系统扩展性好