观察者模式是什么
观察者建立一对多依赖:主题状态变化时通知所有订阅者,通知可以带数据(推送)也可以只发信号(拉取)。事件总线、监听器、响应式流都是它的形态。工程上真正需要设计的是退订、异常隔离与通知顺序,而不是通知这个动作本身。
基本信息
| 意图 | 状态变更触发广播通知 |
|---|---|
| 两种形态 | 推送(携带数据)与拉取(通知后自取) |
| 工程要点 | 退订、异常隔离、通知顺序与幂等 |
| 典型实现 | 监听器注册表、事件总线、消息订阅 |
| 相关取舍 | 同进程内用观察者,跨服务用消息中间件 |
典型使用场景
- 领域事件触发通知与审计记录
- 配置变更后刷新缓存
- 界面或接口层的状态订阅
- 指标与日志的旁路采集
- 插件与扩展点注册回调
Java 示例
Java 示例
interface Listener { void onEvent(Event e); }
class Topic {
private final List<Listener> listeners = new CopyOnWriteArrayList<>();
void subscribe(Listener l) { listeners.add(l); }
void publish(Event e) {
for (Listener l : listeners) l.onEvent(e); // 通知顺序即注册顺序
}
}通知机制的选择
| 机制 | 适用 | 注意 |
|---|---|---|
| 直接方法调用 | 同进程、订阅方很少 | 编译期耦合,扩展要改代码 |
| 观察者与事件总线 | 同进程、多个订阅方 | 需处理退订与异常隔离 |
| 消息中间件 | 跨服务、需要持久化 | 引入投递语义与去重问题 |
订阅者要能优雅退出:退订接口、弱引用或按生命周期销毁都是常见做法;忘记释放的监听器会一直收到回调,也会拖住对象不被回收。
发布方要做的事
- 逐个通知时隔离异常,单个订阅者失败不影响其他订阅者
- 为同步回调设置超时,避免主流程被旁路逻辑拖住
- 明确通知顺序与幂等要求,必要时携带事件编号
- 提供退订入口,并在对象销毁时自动清理订阅
一个订阅者抛异常拖垮整条广播,是观察者最典型的线上问题:发布方要做异常隔离与超时保护,别让旁路逻辑拖住主流程。