什么是观察者模式?它解决什么问题?
简化版
观察者模式是在对象之间建立一种“一对多”的通知关系:当被观察者状态变化时,所有订阅它的观察者都会收到通知。它主要解决对象之间强耦合通知的问题,让事件发布方不需要知道具体有哪些接收方。
详细版
观察者模式属于行为型设计模式,核心思想是把“状态变化”和“变化后的响应动作”解耦。
典型场景是:订单支付成功后,需要发短信、加积分、推送站内信、记录营销事件。如果把这些逻辑都写在支付成功方法里,支付模块会依赖很多下游模块。观察者模式可以让支付模块只发布“支付成功事件”,具体谁响应由观察者自己处理。
它通常包含这些角色:
- Subject:被观察者,也叫主题,负责维护观察者列表并发布通知。
- Observer:观察者接口,定义收到通知后的处理方法。
- ConcreteObserver:具体观察者,实现具体响应逻辑。
- Event:事件对象,携带状态变化所需的数据。
观察者模式的价值是降低发布方和订阅方的耦合,方便新增响应逻辑。但它也会让调用链变隐式,需要注意调试、异常隔离和执行顺序。
完整版教学
一、先理解“为什么需要观察者模式”
很多业务都有“一个动作触发多个后续动作”的特点。
比如用户注册成功后:
- 发送欢迎短信;
- 发放新人券;
- 初始化用户画像;
- 写入运营分析日志;
- 通知推荐系统。
如果这些逻辑全部写在注册服务里,注册服务就会越来越胖:
registerUser();
sendSms();
giveCoupon();
initProfile();
writeLog();
notifyRecommend();
后续每新增一个动作,都要修改注册主流程。注册服务不仅要负责注册,还要知道所有下游处理细节,这就是强耦合。
观察者模式的做法是:注册服务只发布“用户注册成功”这个事实,后续动作由不同观察者自己订阅并处理。
二、观察者模式的核心关系是一对多
观察者模式里,一个被观察者可以对应多个观察者。
Subject 状态变化
-> Observer A
-> Observer B
-> Observer C
被观察者不需要知道观察者具体做什么,只需要调用统一的通知接口。
这就像你关注了一个公众号:公众号发文章时,不需要知道每个读者看完后会收藏、转发还是关闭。它只负责发布,读者自己决定怎么响应。
三、观察者模式解耦的是发布方和响应方
观察者模式不是让代码“少执行”,而是让发布方不再直接依赖响应方。
没有观察者模式时:
注册服务 -> 短信服务
注册服务 -> 优惠券服务
注册服务 -> 推荐服务
使用观察者模式后:
注册服务 -> 发布注册事件
短信观察者 -> 处理注册事件
优惠券观察者 -> 处理注册事件
推荐观察者 -> 处理注册事件
注册服务只关心事件发生,不关心谁处理事件。新增一个“发送站内信观察者”,通常不需要改注册服务。
四、观察者模式常见于哪些地方
观察者模式在工程里非常常见:
- GUI 事件监听:按钮点击后触发监听器;
- Java 事件监听器机制;
- Spring ApplicationEvent;
- 消息通知和业务事件;
- 缓存失效通知;
- 配置变更监听;
- 前端事件绑定;
- 领域事件。
它的共同点是:一个事件发生后,可能有多个处理方感兴趣。
五、观察者模式带来的隐式复杂度
观察者模式降低了直接依赖,但也带来一个问题:调用链不再那么直观。
你看注册服务代码,只能看到它发布了事件;至于有哪些观察者响应,需要去事件注册处、Spring Bean、配置或消息订阅关系里找。
因此真实项目中使用观察者模式时,要做好:
- 事件命名清晰;
- 观察者职责单一;
- 日志记录事件和观察者;
- 异常隔离;
- 避免观察者里做过重逻辑。
六、一对多通知如何解耦
一个支付事件有短信、积分、审计 3 个观察者;发布者只发 1 次通知,新增第 4 个观察者不应修改支付核心流程。
Subject --event--> Observer A / Observer B / Observer C
这个推演把模式带来的收益和成本放在同一条调用链上。设计评审时既要确认结果正确,也要确认额外层次没有改变原有契约。
七、边界、代价与验证
| 检查维度 | 应确认的内容 |
|---|---|
| 正确性 | 发布者不知道具体观察者,不代表系统不需要治理顺序、异常、重复和可观测性。 |
| 适用边界 | 观察者适合附加反应,不适合把必须原子成功的核心步骤随意拆成不可见监听器。 |
| 测试证据 | 覆盖正常路径、异常路径、空值或未知类型,并断言调用次数与顺序 |
| 运行成本 | 记录额外对象、调用层数、延迟、线程或内存开销,不用模式名称代替测量 |
模式落地后要用单元测试验证独立职责,用集成测试验证对象装配和真实调用入口。涉及并发、远程或异步时,还要补充竞态、超时、重复执行与资源释放测试。
易错点:发布者不知道具体观察者,不代表系统不需要治理顺序、异常、重复和可观测性。
八、常见误区与追问
- 误区:观察者模式保证发布者和观察者完全无耦合。 双方仍耦合于事件契约和通知语义,只是去除了对具体实现的直接依赖。
- 误区:发布事件天然等于异步和最终一致性。 观察者只定义一对多通知关系;同步还是异步、是否跨进程以及一致性语义都由具体实现决定。
- 误区:示例代码能运行,就代表模式边界设计正确。 能运行只证明一条路径,仍要检查异常、并发、顺序、生命周期以及新增实现时是否修改稳定代码。
- 追问:新增观察者为什么通常符合开闭原则? 发布者代码不变,只注册新的 Observer 实现即可扩展响应。
- 追问:观察者失败后由谁重试? 模式本身没有答案;同步场景要定义异常传播,异步场景要由执行器、事件总线或消息系统提供重试与死信策略。
- 追问:怎样验证这套设计没有改变原有语义? 先做基线行为测试,再对新增层验证返回值、异常类型、调用次数和顺序;性能敏感处还要比较改造前后的量化指标。
九、加强记忆
观察者模式要记成“一个对象变化,多个订阅者自动响应”。它解决的是发布方和响应方的耦合问题,适合事件通知、监听器和业务扩展点;但使用时要注意调用链隐式、异常隔离和观察者数量失控。