观察者模式
观察者模式:解耦依赖的事件响应机制
一、观察者模式的核心定义
观察者模式(Observer Pattern) 是一种行为型设计模式,定义了对象之间的一对多依赖关系:当一个对象(被观察者 / 主题)的状态发生变化时,所有依赖它的对象(观察者)都会自动收到通知并进行更新。这种模式的核心是解耦被观察者与观察者,让两者无需知晓对方的具体实现,仅通过统一的接口交互。
简单来说,观察者模式就像 “订阅 - 通知” 机制:比如公众号(被观察者)发布新文章时,所有关注该公众号的用户(观察者)都会收到推送,用户无需主动刷新,公众号也无需单独联系每个用户。
二、核心角色与结构
观察者模式包含四个核心角色,各角色职责明确,通过接口实现解耦:
-
抽象主题(Subject):定义被观察者的核心行为,包括注册观察者、移除观察者、通知所有观察者的接口。
-
具体主题(Concrete Subject):抽象主题的实现类,维护观察者列表,当自身状态变化时,调用通知方法触发所有观察者更新。
-
抽象观察者(Observer):定义观察者的更新接口,包含一个接收通知并处理的方法(如
update())。 -
具体观察者(Concrete Observer):抽象观察者的实现类,实现更新接口,根据被观察者的通知执行具体业务逻辑。
结构示意图(简化):
[具体主题] ← 注册/移除 → [抽象观察者]
↑ ↓
通知 实现
↓ ↑
[具体观察者1]、[具体观察者2]、...
三、代码实现示例(Java)
以 “气象站发布天气数据,多个显示终端接收更新” 为例,实现观察者模式:
1. 抽象主题(Subject)
public interface WeatherSubject {
// 注册观察者
void registerObserver(WeatherObserver observer);
// 移除观察者
void removeObserver(WeatherObserver observer);
// 通知所有观察者
void notifyObservers();
}
2. 具体主题(Concrete Subject)
import java.util.ArrayList;
import java.util.List;
public class WeatherData implements WeatherSubject {
// 维护观察者列表
private List<WeatherObserver> observers;
// 被观察的状态(温度、湿度、气压)
private float temperature;
private float humidity;
private float pressure;
public WeatherData() {
observers = new ArrayList<>();
}
// 注册观察者
@Override
public void registerObserver(WeatherObserver observer) {
observers.add(observer);
}
// 移除观察者
@Override
public void removeObserver(WeatherObserver observer) {
observers.remove(observer);
}
// 通知所有观察者(状态变化时调用)
@Override
public void notifyObservers() {
for (WeatherObserver observer : observers) {
// 传递最新状态,触发观察者更新
observer.update(temperature, humidity, pressure);
}
}
// 模拟气象站更新数据(实际场景中可能是传感器采集)
public void setWeatherData(float temperature, float humidity, float pressure) {
this.temperature = temperature;
this.humidity = humidity;
this.pressure = pressure;
// 数据更新后,自动通知观察者
notifyObservers();
}
}
3. 抽象观察者(Observer)
public interface WeatherObserver {
// 接收通知并更新(参数为被观察者的最新状态)
void update(float temperature, float humidity, float pressure);
}
4. 具体观察者(Concrete Observer)
// 手机APP显示终端
public class PhoneDisplay implements WeatherObserver {
@Override
public void update(float temperature, float humidity, float pressure) {
System.out.println("手机APP收到天气更新:");
System.out.printf("温度:%.1f℃,湿度:%.1f%%,气压:%.1f hPa%n",
temperature, humidity, pressure);
}
}
// 电视天气预报终端
public class TVDisplay implements WeatherObserver {
@Override
public void update(float temperature, float humidity, float pressure) {
System.out.println("n电视天气预报更新:");
System.out.printf("今日气温:%.1f℃,空气湿度:%.1f%%,气压稳定在%.1f hPa%n",
temperature, humidity, pressure);
}
}
5. 测试代码
public class ObserverTest {
public static void main(String[] args) {
// 创建被观察者(气象站)
WeatherData weatherData = new WeatherData();
// 创建观察者(显示终端)
WeatherObserver phoneDisplay = new PhoneDisplay();
WeatherObserver tvDisplay = new TVDisplay();
// 注册观察者
weatherData.registerObserver(phoneDisplay);
weatherData.registerObserver(tvDisplay);
// 模拟气象站更新数据
System.out.println("=== 第一次更新天气数据 ===");
weatherData.setWeatherData(25.5f, 60.2f, 1013.2f);
System.out.println("n=== 第二次更新天气数据 ===");
weatherData.setWeatherData(27.8f, 55.0f, 1012.5f);
// 移除手机APP观察者
weatherData.removeObserver(phoneDisplay);
System.out.println("n=== 第三次更新天气数据(已移除手机APP) ===");
weatherData.setWeatherData(23.1f, 70.5f, 1014.0f);
}
}
输出结果
=== 第一次更新天气数据 ===
手机APP收到天气更新:
温度:25.5℃,湿度:60.2%,气压:1013.2 hPa
电视天气预报更新:
今日气温:25.5℃,空气湿度:60.2%,气压稳定在1013.2 hPa
=== 第二次更新天气数据 ===
手机APP收到天气更新:
温度:27.8℃,湿度:55.0%,气压:1012.5 hPa
电视天气预报更新:
今日气温:27.8℃,空气湿度:55.0%,气压稳定在1012.5 hPa
=== 第三次更新天气数据(已移除手机APP) ===
电视天气预报更新:
今日气温:23.1℃,空气湿度:70.5%,气压稳定在1014.0 hPa
四、适用场景
观察者模式适用于以下场景:
-
对象间存在一对多依赖:一个对象状态变化需要联动多个对象响应(如:电商订单支付成功后,通知库存扣减、物流创建、积分增加)。
-
需要解耦依赖关系:避免被观察者与观察者直接耦合(如:消息队列的生产者 - 消费者模型,生产者无需知道消费者是谁)。
-
动态添加 / 移除观察者:需要灵活增减响应对象(如:网站的订阅功能,用户可随时关注 / 取消关注)。
五、优缺点分析
优点
-
解耦性强:被观察者与观察者通过接口交互,互不依赖具体实现,符合 “开闭原则”(新增观察者无需修改被观察者代码)。
-
响应及时:被观察者状态变化时,观察者自动收到通知,无需主动查询(“推模式” 高效)。
-
扩展性好:可随时添加新的观察者,或修改观察者的处理逻辑,不影响整体架构。
缺点
-
通知开销大:若观察者数量过多,被观察者通知所有观察者的过程会消耗较多资源,可能导致响应延迟。
-
循环依赖风险:若观察者与被观察者相互依赖,可能引发循环通知,导致系统崩溃。
-
顺序不可控:默认情况下,观察者的更新顺序由被观察者的通知顺序决定,若需指定顺序需额外处理。
六、扩展与变体
- 推模式 vs 拉模式:
-
推模式(本文示例):被观察者主动将状态数据推送给观察者,观察者被动接收。
-
拉模式:被观察者仅通知 “状态已变”,观察者主动从被观察者获取所需数据(适合观察者需要不同状态的场景)。
-
Java 内置支持:JDK 提供
java.util.Observable(抽象主题)和java.util.Observer(抽象观察者),但因设计缺陷(如Observable是类而非接口,灵活性不足),实际开发中更推荐自定义接口实现。 -
结合其他模式:常与单例模式(确保被观察者唯一)、工厂模式(创建观察者实例)结合使用。

浙公网安备 33010602011771号