Skip to content

什么是观察者模式?一般用在什么场景? ​

  • 标签:设计模式

🏛 业务架构师 ​

观察者模式定义对象间一对多的依赖:被观察者状态变化,自动通知所有观察者。业务上它就是事件驱动的雏形。

我用"下单成功"这个电商最核心的时刻讲它的价值:订单服务只负责落订单并发一个 OrderCreated 事件,积分、短信、优惠券核销、推荐召回、数据仓库各自订阅处理——核心域不认识任何下游。带来的业务收益是实打实的:

  • 新业务零侵入接入:营销想在下单后发张券,订阅事件即可,订单服务一行不改、一次不发版——上游稳定性等于下游迭代自由度;
  • 故障隔离:短信服务挂了不影响下单,事件积压在 MQ 里慢慢消化,而不是把下单链路拖挂;
  • 领域边界清晰:事件就是 DDD 里限界上下文之间的集成契约,谁发布、谁订阅、契约版本怎么演进,都成了显式治理对象。

落地注意三点:事件命名用业务语言(过去时态,OrderCreated 而非 CreateOrder);观察者处理必须幂等(通知会重发);跨进程直接上 MQ 的发布订阅,进程内用 Spring 的 ApplicationEvent 即可,不要进程内也引 MQ 增加运维面。

反例提醒:观察者链条过深时(A 的事件触发 B,B 又触发 C),调用链路会失控,事件流要画图治理。模式是杠杆,杠杆要有支点——支点就是事件契约管理。

🏗 系统架构师 ​

观察者模式定义对象间一对多依赖:被观察者状态变化时自动通知所有观察者,核心是事件源的解耦。

结构:Subject 维护 Observer 列表,状态变更遍历通知;观察者只依赖抽象接口。

典型场景:

  • Spring 事件机制:发布 ApplicationEvent,@EventListener 消费。我下单服务里订单创建后发 OrderCreatedEvent,发券、积分、通知各自监听,主流程不关心谁在消费,新增旁路零改动;
  • 分布式形态就是 MQ 的发布订阅,观察者模式是消息驱动的本地雏形;
  • GUI 事件、Zookeeper/配置中心的 Watcher、Guava EventBus。

工程要点(踩过的坑):

  • 监听器默认同步执行,一个慢监听拖垮主流程,所以旁路监听一律 @Async + 独立线程池,并配异常处理(监听器异常不能影响发布方);
  • 异步后就有了最终一致问题,关键数据要考虑本地消息表/事务提交后再发布(@TransactionalEventListener 的 AFTER_COMMIT),避免事务回滚了事件已发出的脏通知。

💻 全栈开发专家 ​

被观察者状态变化时自动通知所有订阅的观察者,一对多的依赖解耦——主体不关心谁在听。

Spring 事件是最好用的落地形态:

java
// 1. 定义事件
public class OrderCreatedEvent extends ApplicationEvent {
    private final Long orderId;
    ...
}
// 2. 主流程发布(事务内)
applicationEventPublisher.publishEvent(new OrderCreatedEvent(this, orderId));
// 3. 监听者各自处理
@EventListener
public void addPoints(OrderCreatedEvent e) { ... }

// 关键注解:事务提交后才执行,避免下游查不到数据
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
public void sendSms(OrderCreatedEvent e) { ... }

我用它的场景:下单后发短信/加积分/风控送审、用户注册后初始化数据、缓存失效广播。

踩过的坑:

  1. 默认 @EventListener 是同步执行且在事务内,监听器抛异常会回滚主事务——这就是我必用 @TransactionalEventListener(AFTER_COMMIT) + @Async 的原因;
  2. 异步后要自己处理线程池和异常日志,否则失败无声无息;
  3. 观察者链路长时排查困难,日志里必须带事件源 traceId。跨服务场景就升级为 MQ 广播。