1. 观察者模式的核心价值与应用场景
观察者模式(Observer Pattern)是软件设计中一个经典的行为型模式,它定义了对象之间一对多的依赖关系。当被观察对象(Subject)状态发生变化时,所有依赖于它的观察者(Observer)都会自动收到通知并更新。这种机制完美解决了对象间的动态联动问题,避免了紧耦合带来的维护难题。
我在实际项目中最常用到观察者模式的场景是用户界面的事件处理。比如一个电商后台管理系统,当商品库存数量发生变化时,需要同时更新:
- 前端库存显示组件
- 库存预警系统
- 销售数据分析看板
- 物流调度系统
如果采用传统的直接调用方式,商品类需要硬编码调用所有相关模块的更新方法。而使用观察者模式后,商品类只需维护观察者列表,状态变化时统一通知即可。新增订阅方也无需修改被观察者代码,系统扩展性大幅提升。
2. 模式结构与核心组件解析
2.1 标准UML类图实现
观察者模式的标准实现包含两个核心接口和若干具体实现类:
Subject (被观察者接口) |—— registerObserver() |—— removeObserver() |—— notifyObservers() ConcreteSubject (具体被观察者) |—— observers: List<Observer> |—— state: Object |—— setState() Observer (观察者接口) |—— update() ConcreteObserver (具体观察者) |—— update()2.2 关键组件职责说明
Subject接口:
- 定义注册(registerObserver)、删除(removeObserver)和通知(notifyObservers)观察者的方法
- 维护观察者列表(通常用List或Set存储)
ConcreteSubject:
- 实现具体业务状态(如订单状态、库存数量等)
- 状态变更时调用notifyObservers()触发通知
- 示例代码片段:
public class ProductInventory implements Subject { private List<Observer> observers = new ArrayList<>(); private int stock; public void setStock(int newStock) { this.stock = newStock; notifyObservers(); } @Override public void notifyObservers() { for (Observer o : observers) { o.update(stock); } } }
Observer接口:
- 定义update()方法约定响应逻辑
- 参数通常传递状态数据或事件对象
ConcreteObserver:
- 实现具体的响应逻辑
- 示例代码:
public class StockAlert implements Observer { @Override public void update(int stock) { if (stock < 10) { sendAlert("库存预警:当前仅剩" + stock + "件"); } } }
3. 五种典型实现方式与选型建议
3.1 同步阻塞式实现
最基础的实现方式,被观察者直接遍历调用所有观察者的update方法。特点是:
- 简单直接,适合观察者较少且处理快的场景
- 被观察者需要等待所有观察者处理完成
- 某个观察者异常会影响整体通知流程
// 经典实现示例 public void notifyObservers() { for (Observer o : observers) { o.update(data); // 同步调用 } }3.2 异步非阻塞实现
引入线程池实现异步通知,避免阻塞主流程:
private ExecutorService executor = Executors.newCachedThreadPool(); public void notifyObservers() { for (Observer o : observers) { executor.submit(() -> o.update(data)); } }适用场景:
- 观察者处理耗时较长(如IO操作)
- 需要保证被观察者执行效率
- 观察者之间不需要保证执行顺序
3.3 事件总线模式
通过中央事件总线解耦,被观察者发布事件,观察者订阅特定事件类型:
// 使用Google Guava EventBus EventBus bus = new EventBus(); bus.register(new StockObserver()); // 注册观察者 // 被观察者发布事件 bus.post(new StockChangeEvent(newStock));优势:
- 完全解耦观察双方
- 支持基于事件类型的过滤
- 天然支持异步处理
3.4 响应式流实现
使用RxJava等响应式框架:
Subject<Integer> subject = PublishSubject.create(); // 观察者订阅 subject.subscribe(stock -> checkStock(stock)); // 被观察者发出事件 subject.onNext(newStock);特点:
- 强大的事件流处理能力
- 支持背压、错误处理等高级特性
- 适合复杂事件处理场景
3.5 Spring事件机制
Spring框架内置的事件发布-订阅模型:
// 定义事件 public class StockEvent extends ApplicationEvent { public StockEvent(Integer stock) { super(stock); } } // 发布事件 applicationContext.publishEvent(new StockEvent(stock)); // 监听事件 @Component public class StockListener { @EventListener public void handleEvent(StockEvent event) { // 处理逻辑 } }选型建议:
- 简单场景:同步阻塞式
- 分布式系统:消息队列(RabbitMQ/Kafka)
- Web应用:Spring事件
- 复杂事件流:RxJava/Reactor
- 完全解耦:事件总线
4. 实战中的七个关键问题与解决方案
4.1 观察者执行顺序控制
问题:多个观察者需要按特定顺序执行(如先验证后处理)
解决方案:
- 使用有序集合(如LinkedList)存储观察者
- 引入优先级字段:
public interface Observer extends Comparable<Observer> { int getPriority(); default int compareTo(Observer o) { return Integer.compare(getPriority(), o.getPriority()); } } - 使用@Order注解(Spring环境)
4.2 避免通知风暴
问题:被观察者频繁变化导致观察者被过度触发
解决方案:
- 添加状态变化检查:
public void setStock(int newStock) { if (this.stock != newStock) { // 只有实际变化才通知 this.stock = newStock; notifyObservers(); } } - 添加节流(throttling)机制
- 批量处理模式(如每10秒收集变化后统一通知)
4.3 循环依赖检测
问题:观察者间接导致被观察者状态变化,形成无限循环
解决方案:
- 添加触发标记:
private boolean notifying = false; public void notifyObservers() { if (notifying) return; notifying = true; try { // 实际通知逻辑 } finally { notifying = false; } } - 使用事件ID追踪,丢弃已处理事件
4.4 观察者异常处理
问题:某个观察者异常导致其他观察者无法收到通知
解决方案:
- 单独捕获每个观察者的异常:
public void notifyObservers() { for (Observer o : observers) { try { o.update(data); } catch (Exception e) { log.error("Observer failed", e); } } } - 使用Dead Letter Queue存储失败事件
- 熔断机制(连续失败N次后临时移除观察者)
4.5 内存泄漏预防
问题:观察者未正确注销导致无法回收
解决方案:
- 显式注销机制:
public void removeObserver(Observer o) { observers.remove(o); } - 使用WeakReference存储观察者
- 生命周期绑定(如Android的Lifecycle)
4.6 分布式场景扩展
问题:跨JVM/跨服务的观察者通知
解决方案:
- 基于消息队列(Kafka/RabbitMQ):
- 被观察者发布消息到特定topic
- 观察者订阅对应topic
- 使用Redis Pub/Sub
- 分布式事件总线(如Axon Framework)
4.7 性能优化技巧
- 观察者列表使用CopyOnWriteArrayList避免并发修改
- 高频场景使用无锁数据结构
- 延迟通知(合并短时间内的多次变化)
- 选择性通知(仅通知关心特定变化的观察者)
5. 经典框架中的观察者模式实现
5.1 Java标准库实现
java.util.Observable(已过时)和Observer接口:
// 被观察者 public class StockObservable extends Observable { public void setStock(int stock) { setChanged(); // 标记状态变化 notifyObservers(stock); } } // 观察者 public class StockDisplay implements Observer { @Override public void update(Observable o, Object arg) { System.out.println("库存更新:" + arg); } }注意:Java 9后Observable被标记为过时,推荐使用PropertyChangeListener等替代方案。
5.2 Spring事件机制
Spring框架提供了完善的事件发布-订阅支持:
- 自定义事件继承ApplicationEvent
- 使用ApplicationEventPublisher发布事件
- 通过@EventListener或实现ApplicationListener接口订阅事件
优势:
- 与Spring容器深度集成
- 支持异步事件处理(@Async)
- 支持事务绑定的事件发布
5.3 Android中的观察者模式
Android大量使用观察者模式,典型实现包括:
- LiveData - 生命周期感知的数据持有者
- ViewModel - 界面数据观察
- BroadcastReceiver - 系统广播监听
示例:
// 观察LiveData viewModel.stockLiveData.observe(this) { stock -> updateUI(stock) }5.4 Reactor/RxJava响应式流
响应式编程库将观察者模式发展到新高度:
Flux<Integer> stockStream = Flux.just(100, 95, 90...); stockStream .filter(stock -> stock < 50) .subscribe(stock -> sendAlert(stock));特性:
- 丰富的操作符(map/filter/flatMap等)
- 背压支持
- 线程调度控制
6. 模式变体与进阶应用
6.1 中介者模式结合
通过引入中介者管理复杂的观察关系:
Subject -> Mediator -> Observer1 -> Observer2 -> Observer3优势:
- 减少观察者之间的直接依赖
- 集中管理复杂的通知逻辑
- 便于动态调整观察关系
6.2 事件溯源模式
将状态变化记录为事件序列:
public interface Event { String getId(); long getTimestamp(); } public class StockIncreased implements Event { private final int amount; // ... }观察者通过重放事件序列重建状态,适合审计、回放等场景。
6.3 数据绑定实现
现代前端框架的数据绑定本质是观察者模式的高级应用:
// Vue.js示例 data() { return { stock: 100 } }, watch: { stock(newVal) { console.log('库存变化:', newVal) } }6.4 CQRS架构中的应用
在命令查询职责分离架构中,观察者模式用于:
- 将写模型的变化同步到读模型
- 更新物化视图
- 触发后续处理流程
典型流程:
Command -> 修改聚合根 -> 发布领域事件 -> 观察者更新读模型7. 设计权衡与替代方案
7.1 观察者模式 vs 发布订阅模式
关键区别:
耦合程度:
- 观察者:被观察者知道观察者存在
- 发布订阅:通过中间渠道完全解耦
通信方式:
- 观察者:通常是同步调用
- 发布订阅:通常异步处理
使用场景:
- 观察者:单应用内部通信
- 发布订阅:跨系统/分布式通信
7.2 观察者模式 vs 回调机制
相似点:都是事件通知机制 不同点:
观察者:
- 标准化的接口约定
- 支持多观察者
- 生命周期管理完善
回调:
- 通常是一对一关系
- 接口形式灵活
- 更适合简单场景
7.3 何时不使用观察者模式
以下情况考虑其他方案:
- 观察者数量极少(直接调用更简单)
- 通知链路非常长(考虑责任链模式)
- 需要严格保证事件顺序(考虑队列)
- 观察者处理耗时严重影响主流程(考虑异步消息)
7.4 性能考量与优化
大规模观察者系统的优化方向:
- 按需通知(观察者声明关注的事件类型)
- 分层通知(先通知一级观察者,再扩散)
- 增量通知(仅传递变化部分)
- 合并通知(多个变化合并为一个事件)