生活所迫呀。
观察者模式是一种行为型设计模式,它定义了一种对象之间一对多的依赖关系,使得当一个对象的状态发生改变时,所有依赖它的对象都会自动收到通知并更新。
为什么要用观察者模式?我们有下面几个原因:
- 解耦通知逻辑:观察者模式的核心价值在于:发布者(被观察者)和订阅者(观察者)之间是解耦的。 发布者无需知道观察者是谁、做什么,只需要在状态发生变化时“通知大家”;观察者则只关心“是否订阅了该对象”,无需知道发布者的具体业务逻辑。
- 自动通知,避免轮询:在没有观察者模式的情况下,我们可能需要让每个组件不断检查(轮询)状态的变化。这不仅效率低,还容易写出重复、混乱的代码。
- 事件驱动架构的基础:现代前后端框架(如 React、Vue、Java Swing、Spring)中大量使用观察者模式或其变种(发布-订阅模式)来构建响应式系统。
示例
我们还是以实际的需求入手,举一个最经典的例子:
你正在开发一个气象站系统,它能实时监测温度、湿度、气压等数据。现在有多个布告板(显示面板)希望自动接收到最新的天气数据并显示,比如:
- 当前天气布告板(CurrentConditionsDisplay)
- 统计布告板(StatisticsDisplay)
- 预报布告板(ForecastDisplay)
一旦气象站更新数据,所有布告板都应立即更新并展示最新信息。
首先,我们需要定义一个被观察者接口Subject:
1 2 3 4 5
| public interface Subject { public void registerObserver(Observer o); public void removeObserver(Observer o); public void notifyObservers(); }
|
其规范了三项功能:
当然,我们也需要定义布告板观察者接口Observer:
1 2 3
| public interface Observer { public void update(float temp, float humidity, float pressure); }
|
额外的,我们定义一个展示接口DisplayElement,当布告板需要显示时,调用方法display():
1 2 3
| public interface DisplayElement { public void display(); }
|
定义完接口后,我们需要定义类来实现主题了。首先,定义一个实现被观察者接口Subject的类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
| 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(); } }
|
随后,定义布告板(观察者)
1 2 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 + "%"); } }
|
测试一下这个程序:
1 2 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):
1 2 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定义一个观察者(布告板)
1 2 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
)。
注册观察者、更新数据、触发通知:
1 2 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)或响应式编程,在后面的章节中,我们会讲到。