一、核心定义与角色差异
-
观察者模式(Observer Pattern)
-
核心角色:
- Subject (被观察者):直接维护
Observer
列表,提供注册/注销接口,状态变更时主动遍历列表通知所有观察者。 - Observer (观察者):定义统一回调方法(如
update()
),接收Subject
的变更通知。
- Subject (被观察者):直接维护
-
通信方式:
- 直接绑定,
Subject
与Observer
强耦合,无中间层。
jsclass Subject { constructor() { this.observers = []; } add(observer) { this.observers.push(observer); } notify(data) { this.observers.forEach(obs => obs.update(data)); } } class Observer { update(data) { console.log(`收到更新:${data}`); } } //使用 class MyObserver extends Observer{ constructor(){ super() } // 重写父类方法 notify(data){ console.log(`MyObserver数据更新:${data}`) } } const sj = new Subject(); const mo = new MyObserver(); sj.add(mo); sj.update("更新完成"); // MyObserver数据更新:更新完成
- 直接绑定,
-
-
发布订阅模式(Pub-Sub Pattern)
-
核心角色:
- Publisher (发布者):仅负责向中间层(
Broker
)发布事件,不感知订阅者。 - Subscriber(订阅者):通过中间层订阅特定主题(Topic),执行自定义回调。
- Broker(消息中心):管理主题与订阅关系,负责消息路由和分发。
- Publisher (发布者):仅负责向中间层(
-
通信方式:
- 通过中间层解耦,
Publisher
和Subscriber
互不感知,基于主题或事件类型通信。
jsclass EventBus { constructor() { this.topics = {}; } subscribe(topic, callback) { this.topics[topic] = this.topics[topic] || []; this.topics[topic].push(callback); } publish(topic, data) { this.topics[topic]?.forEach(cb => cb(data)); } } // 使用 const ev = new EventBus(); ev.subscribe("myName", data =>{ console.log(data) }); ev.publish("myName", "触发myName成功");
- 通过中间层解耦,
-
二、关键区别对比
维度 | 观察者模式 | 发布订阅模式 | 来源 |
---|---|---|---|
耦合度 | 高耦合(Subject 直接管理 Observer ) |
低耦合(通过 Broker 解耦) |
13 |
通信方向 | 单向:Subject → Observer |
多向:支持多发布者、多订阅者 | 34 |
中间层 | 无 | 必须存在 Broker 或事件中心 |
14 |
事件处理粒度 | 所有 Observer 执行相同逻辑(如 update() ) |
基于不同主题执行差异化回调 | 58 |
扩展性 | 动态增删 Observer 较灵活 |
支持动态订阅/取消、跨模块通信 | 34 |
异步支持 | 通常同步执行 | 天然支持异步(如消息队列延迟分发) | 58 |
三、适用场景
-
观察者模式适用场景
- UI 组件状态同步:如 Vue 的响应式数据依赖追踪。
- 原生事件监听:如 DOM 事件(点击、滚动)的绑定与触发。
- 局部状态管理:父子组件或同一模块内的直接通信。
-
发布订阅模式适用场景
- 跨组件/模块通信 :如 Vue 的
EventBus
、React 的全局事件总线。 - 分布式系统消息传递:微服务间通过消息队列解耦通信14。
- 动态订阅场景:如实时聊天室、新闻推送(用户按兴趣订阅)。
- 跨组件/模块通信 :如 Vue 的
四、实现选择建议
-
优先观察者模式的情况
- 需要简单的一对多通知逻辑,且通信双方关系明确。
- 高频同步操作(如游戏引擎的状态更新)。
-
优先发布订阅模式的情况
- 需要解耦多对多通信、支持动态订阅/取消。
- 异步场景(如 HTTP 请求结果分发)或跨项目模块通信。
五、实战示例
-
观察者模式:DOM 事件监听
jsconst button = document.querySelector('button'); button.addEventListener('click', (e) => { console.log('按钮被点击!'); // Observer 逻辑 });
-
发布订阅模式:全局事件总线
jsconst eventBus = new EventBus(); // 订阅 eventBus.subscribe('message', (data) => { console.log(`收到消息:${data}`); }); // 发布 eventBus.publish('message', 'Hello Pub-Sub!');
六、总结
- 观察者模式 :适用于 强绑定、直接通信 的场景,实现简单但扩展性受限。
- 发布订阅模式 :适用于 解耦、动态、异步 的复杂场景,通过中间层提升灵活性。
- 选择依据 :根据 耦合需求、通信复杂度、性能要求 综合权衡,避免滥用单一模式。