生活所迫呀。
观察者模式是一种行为型设计模式,它定义了一种对象之间一对多的依赖关系,使得当一个对象的状态发生改变时,所有依赖它的对象都会自动收到通知并更新。
为什么要用观察者模式?我们有下面几个原因:
- 解耦通知逻辑:观察者模式的核心价值在于:发布者(被观察者)和订阅者(观察者)之间是解耦的。  发布者无需知道观察者是谁、做什么,只需要在状态发生变化时“通知大家”;观察者则只关心“是否订阅了该对象”,无需知道发布者的具体业务逻辑。
- 自动通知,避免轮询:在没有观察者模式的情况下,我们可能需要让每个组件不断检查(轮询)状态的变化。这不仅效率低,还容易写出重复、混乱的代码。
- 事件驱动架构的基础:现代前后端框架(如 React、Vue、Java Swing、Spring)中大量使用观察者模式或其变种(发布-订阅模式)来构建响应式系统。
 示例
我们还是以实际的需求入手,举一个最经典的例子:
你正在开发一个气象站系统,它能实时监测温度、湿度、气压等数据。现在有多个布告板(显示面板)希望自动接收到最新的天气数据并显示,比如:
- 当前天气布告板(CurrentConditionsDisplay)
- 统计布告板(StatisticsDisplay)
- 预报布告板(ForecastDisplay)
 一旦气象站更新数据,所有布告板都应立即更新并展示最新信息。
首先,我们需要定义一个被观察者接口Subject:
| 12
 3
 4
 5
 
 | public interface Subject {  public void registerObserver(Observer o);
 public void removeObserver(Observer o);
 public void notifyObservers();
 }
 
 | 
其规范了三项功能:
当然,我们也需要定义布告板观察者接口Observer:
| 12
 3
 
 | public interface Observer {  public void update(float temp, float humidity, float pressure);
 }
 
 | 
额外的,我们定义一个展示接口DisplayElement,当布告板需要显示时,调用方法display():
| 12
 3
 
 | public interface DisplayElement {  public void display();
 }
 
 | 
定义完接口后,我们需要定义类来实现主题了。首先,定义一个实现被观察者接口Subject的类WeatherData:
| 12
 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
 
 | import java.util.ArrayList;  
 public class WeatherData implements Subject{
 private ArrayList 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 (int i = 0; i<observers.size(); i++){
 Observer observer = (Observer) observers.get(i);
 observer.update(temperature, humidity, pressure);
 }
 }
 
 
 public void setMeasurements(float temperature, float humidity, float pressure){
 this.temperature = temperature;
 this.humidity = humidity;
 this.pressure = pressure;
 notifyObservers();
 }
 }
 
 | 
随后,定义布告板(观察者)
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 
 | public class CurrentConditionsDisplay implements Observer, DisplayElement {private float temperature;
 private float humidity;
 
 public CurrentConditionsDisplay(Subject weatherData) {
 weatherData.registerObserver(this);
 }
 
 public void update(float temperature, float humidity, float pressure) {
 this.temperature = temperature;
 this.humidity = humidity;
 display();
 }
 
 public void display() {
 System.out.println("当前天气:温度 = " + temperature + "°C,湿度 = " + humidity + "%");
 }
 }
 
 | 
测试一下这个程序:
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 
 | public class WeatherStation {public static void main(String[] args) {
 WeatherData weatherData = new WeatherData();
 
 CurrentConditionsDisplay currentDisplay = new CurrentConditionsDisplay(weatherData);
 
 weatherData.setMeasurements(30.4f, 65.2f, 1013.1f);
 weatherData.setMeasurements(28.9f, 70.1f, 1012.5f);
 }
 }
 
 | 
 java内置写法
Java 在早期(JDK 1.0)就内置了对观察者模式的支持:
- java.util.Observable:被观察者(Subject)
- java.util.Observer:观察者(Observer)
还是使用上面气象站的例子,首先,我们继承内置的类Observable定义一个被观察者(WeatherData):
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 
 | import java.util.Observable;
 public class WeatherData 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;
 
 setChanged();
 notifyObservers();
 }
 
 public float getTemperature() { return temperature; }
 public float getHumidity() { return humidity; }
 public float getPressure() { return pressure; }
 }
 
 | 
setChanged()是 Observable 提供的方法,必须调用这个方法,告诉系统“我准备好通知别人了”。如果不调用 setChanged(),后续的 notifyObservers() 什么都不会发生。
notifyObservers():遍历当前注册的所有观察者(通过 addObserver() 添加的),并依次调用它们的 update() 方法。默认参数为 null,也可以使用 notifyObservers(Object arg) 传递数据。
随后,继承内置类Observer定义一个观察者(布告板)
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 
 | import java.util.Observer;import java.util.Observable;
 
 public class CurrentConditionsDisplay implements Observer {
 public void update(Observable o, Object arg) {
 if (o instanceof WeatherData) {
 WeatherData wd = (WeatherData) o;
 System.out.println("当前天气:温度 = " + wd.getTemperature() + "°C,湿度 = " + wd.getHumidity() + "%");
 }
 }
 }
 
 | 
update(Observable o, Object arg): 当被观察者调用 notifyObservers() 时,每个注册的观察者的 update() 方法都会被执行。参数 o 是被观察者对象本身(即 WeatherData)。参数 arg 是通过 notifyObservers(arg) 传递的附加数据(如果调用的是 notifyObservers(),则为 null)。
注册观察者、更新数据、触发通知:
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 
 | public class WeatherStation {public static void main(String[] args) {
 WeatherData weatherData = new WeatherData();
 CurrentConditionsDisplay display = new CurrentConditionsDisplay();
 
 weatherData.addObserver(display);
 
 
 weatherData.setMeasurements(25.6f, 65.0f, 1012.0f);
 }
 }
 
 | 
虽然 Java 提供了这个内置机制,但它有一些明显缺陷,因此在 JDK 9 后已标记为过时(Deprecated)。 缺点如下:
- Observable是一个类,不是接口,限制了继承(Java 不支持多继承)
- 内部方法如 setChanged()并非自动调用,容易遗漏
- update()方法传参设计不灵活
- 不支持泛型
因此,实际上用我们一开始写的方式自定义 Subject 和 Observer 接口是最清晰和通用的方式。哈哈。
当我们在大型项目中,面临越来越复杂的模块通信、异步响应、数据流处理需求时,传统的观察者模式(无论是自定义接口还是 Java 原生的 Observer/Observable)都可能显得不够灵活、不够强大,可能需要涉及到事件驱动架构(EDA)或响应式编程,在后面的章节中,我们会讲到。