观察者模式和发布订阅模式有什么区别?
简化版
观察者模式通常是主题直接维护观察者并通知它们,发布方和订阅方仍通过主题存在一定关联;发布订阅模式通常引入事件通道、消息队列或事件总线,发布方和订阅方不直接知道彼此,解耦更彻底。
详细版
两者都属于事件通知思想,但结构层次不同。
观察者模式:
- Subject 持有 Observer 列表;
- Subject 直接调用 Observer;
- 常用于进程内对象通知;
- 结构简单,调用关系相对直接。
发布订阅模式:
- 发布者把消息发到 Broker、Topic 或 EventBus;
- 订阅者订阅某类主题;
- 发布者和订阅者不直接关联;
- 常用于跨模块、跨进程、异步消息场景。
可以理解为:发布订阅是观察者思想在事件中介层上的工程化扩展。比如手写监听器更像观察者模式,Kafka、RabbitMQ、Redis Pub/Sub、事件总线更像发布订阅模式。
完整版教学
一、观察者模式是直接通知
经典观察者模式中,Subject 知道有哪些 Observer。
Subject
-> Observer A
-> Observer B
-> Observer C
Subject 通知时会遍历观察者列表,直接调用观察者方法。
这种结构适合进程内、对象级别的通知。例如 GUI 按钮点击后通知监听器,配置对象变化后通知监听者。
二、发布订阅模式引入中介
发布订阅模式中,发布者通常不直接保存订阅者列表,而是把消息交给一个中介。
Publisher -> Topic/Broker/EventBus -> Subscriber
发布者只知道自己发布了某个主题的消息,订阅者只知道自己订阅了某个主题。双方不直接依赖。
这让系统更适合异步化、跨服务通信和削峰填谷。
三、核心区别是有没有中间层
观察者模式和发布订阅模式最关键的区别是:通知是否通过独立中间层转发。
观察者模式里:
- 主题通常负责保存观察者;
- 主题直接调用观察者;
- 通知关系在代码里比较明确。
发布订阅模式里:
- 发布者不直接保存订阅者;
- 消息由事件总线、Broker 或 Topic 管理;
- 订阅关系更动态,也更隐式。
所以发布订阅的解耦程度更高,但排查链路也更复杂。
四、同步和异步不是唯一判断标准
很多人会说“观察者是同步,发布订阅是异步”,这个说法不够严谨。
观察者模式可以做异步通知,比如主题遍历观察者时把任务丢到线程池。
发布订阅也可以在进程内同步调用,比如某些 EventBus 可以同步分发事件。
因此同步异步不是本质区别。本质区别仍然是发布方和订阅方之间是否通过中介层解耦。
五、工程中如何选择
如果是同一个进程内的轻量事件,观察者模式或 Spring 事件就够了。
如果需要跨服务、可靠投递、失败重试、削峰、消费组、消息堆积能力,就应该考虑消息队列形式的发布订阅。
例如:
- 用户界面点击按钮:观察者模式;
- Spring 应用内发布领域事件:进程内事件机制;
- 订单支付后通知库存、物流、营销系统:消息队列发布订阅更稳。
六、直接引用和中介路由的差异
进程内 Subject 直接遍历 5 个 Observer,需要维护 5 个引用;发布订阅通过 Broker 按 topic 路由,发布者持有 0 个订阅者引用。
observer: Subject -> Observer; pubsub: Publisher -> Broker/topic -> Subscriber
这个推演把模式带来的收益和成本放在同一条调用链上。设计评审时既要确认结果正确,也要确认额外层次没有改变原有契约。
七、边界、代价与验证
| 检查维度 | 应确认的内容 |
|---|---|
| 正确性 | 同步异步不是唯一标准;核心要看发布者是否直接管理订阅关系,还是由中介解耦。 |
| 适用边界 | Broker 能带来跨进程、持久化和异步能力,但也增加运维、序列化、重复投递和一致性成本。 |
| 测试证据 | 覆盖正常路径、异常路径、空值或未知类型,并断言调用次数与顺序 |
| 运行成本 | 记录额外对象、调用层数、延迟、线程或内存开销,不用模式名称代替测量 |
模式落地后要用单元测试验证独立职责,用集成测试验证对象装配和真实调用入口。涉及并发、远程或异步时,还要补充竞态、超时、重复执行与资源释放测试。
易错点:同步异步不是唯一标准;核心要看发布者是否直接管理订阅关系,还是由中介解耦。
八、常见误区与追问
- 误区:只要异步通知就是发布订阅。 本地观察者也可异步执行,发布订阅也可能同步转发,不能用线程模型单独判断。
- 误区:发布事件天然等于异步和最终一致性。 观察者只定义一对多通知关系;同步还是异步、是否跨进程以及一致性语义都由具体实现决定。
- 误区:示例代码能运行,就代表模式边界设计正确。 能运行只证明一条路径,仍要检查异常、并发、顺序、生命周期以及新增实现时是否修改稳定代码。
- 追问:什么时候从观察者升级到消息系统? 需要跨进程、持久化、削峰、重试或独立扩缩容时才值得承担中间件成本。
- 追问:观察者失败后由谁重试? 模式本身没有答案;同步场景要定义异常传播,异步场景要由执行器、事件总线或消息系统提供重试与死信策略。
- 追问:怎样验证这套设计没有改变原有语义? 先做基线行为测试,再对新增层验证返回值、异常类型、调用次数和顺序;性能敏感处还要比较改造前后的量化指标。
九、加强记忆
观察者模式和发布订阅模式都在做事件通知,但观察者偏“主题直接通知观察者”,发布订阅偏“通过中间层按主题转发消息”。有中介层、跨进程、异步解耦、可靠投递需求时,更接近发布订阅模式。