您现在的位置是:网站首页 > 观察者模式(Observer)与发布/订阅的区别文章详情
观察者模式(Observer)与发布/订阅的区别
陈川
【
JavaScript
】
10112人已围观
5306字
观察者模式(Observer)和发布/订阅(Pub/Sub)都是用于处理对象间一对多依赖关系的设计模式,但它们在实现方式和耦合度上有显著差异。观察者模式要求观察者直接订阅目标对象,而发布/订阅通过中间代理解耦发布者和订阅者。
观察者模式的核心机制
观察者模式包含两个主要角色:
- Subject(目标):维护观察者列表,提供添加/删除观察者的方法,状态变化时通知所有观察者
- Observer(观察者):定义接收通知的更新接口
class Subject {
constructor() {
this.observers = [];
}
addObserver(observer) {
this.observers.push(observer);
}
removeObserver(observer) {
this.observers = this.observers.filter(obs => obs !== observer);
}
notify(data) {
this.observers.forEach(observer => observer.update(data));
}
}
class Observer {
update(data) {
console.log(`Received data: ${data}`);
}
}
// 使用示例
const subject = new Subject();
const observer1 = new Observer();
const observer2 = new Observer();
subject.addObserver(observer1);
subject.addObserver(observer2);
subject.notify('State changed!'); // 两个观察者都会收到通知
发布/订阅模式的实现特点
发布/订阅模式引入事件通道作为中介:
- Publisher(发布者):将消息发布到特定频道
- Subscriber(订阅者):向事件通道订阅特定频道
- Event Channel(事件通道):管理频道与订阅关系
class PubSub {
constructor() {
this.channels = {};
}
subscribe(channel, callback) {
if (!this.channels[channel]) {
this.channels[channel] = [];
}
this.channels[channel].push(callback);
}
publish(channel, data) {
if (this.channels[channel]) {
this.channels[channel].forEach(callback => callback(data));
}
}
}
// 使用示例
const pubsub = new PubSub();
// 订阅者A订阅news频道
pubsub.subscribe('news', data => {
console.log(`Subscriber A received: ${data}`);
});
// 订阅者B订阅news频道
pubsub.subscribe('news', data => {
console.log(`Subscriber B received: ${data}`);
});
// 发布消息到news频道
pubsub.publish('news', 'New article published!');
两种模式的关键差异
-
耦合程度不同
- 观察者模式:Subject和Observer相互知晓
- 发布/订阅:发布者和订阅者通过事件通道完全解耦
-
同步机制差异
- 观察者模式通常是同步的,调用notify()会立即触发所有观察者
- 发布/订阅可以是异步的,事件通道可以控制消息分发时机
-
关系管理方式
- 观察者模式:Subject直接管理观察者列表
- 发布/订阅:由独立的事件通道维护订阅关系
-
适用场景
- 观察者模式适合组件间强关联的场景(如React的状态管理)
- 发布/订阅适合跨系统、跨层级的消息通信(如微前端架构)
实际应用场景对比
观察者模式典型用例:
// Vue响应式系统实现原理简化版
class Dep {
constructor() {
this.subscribers = [];
}
depend() {
if (target && !this.subscribers.includes(target)) {
this.subscribers.push(target);
}
}
notify() {
this.subscribers.forEach(sub => sub());
}
}
let target = null;
function watcher(fn) {
target = fn;
fn();
target = null;
}
const dep = new Dep();
watcher(() => {
dep.depend();
console.log('视图更新');
});
dep.notify(); // 触发视图更新
发布/订阅模式典型用例:
// Redux的store实现原理简化版
function createStore(reducer) {
let state;
const listeners = [];
const subscribe = listener => {
listeners.push(listener);
return () => {
listeners = listeners.filter(l => l !== listener);
};
};
const dispatch = action => {
state = reducer(state, action);
listeners.forEach(listener => listener());
};
const getState = () => state;
dispatch({}); // 初始化state
return { subscribe, dispatch, getState };
}
性能与复杂度考量
-
内存消耗
- 观察者模式:每个Subject维护自己的观察者列表,可能造成重复存储
- 发布/订阅:集中式事件通道可能成为单点瓶颈
-
调试难度
- 观察者模式:调用链路直接可见,容易跟踪
- 发布/订阅:消息流转路径不透明,需要额外日志工具
-
扩展性
- 观察者模式:新增观察者类型需要修改Subject接口
- 发布/订阅:可以动态添加新频道而不影响现有结构
混合使用的情况
现代框架常结合两种模式的优势:
// 混合模式示例:观察者模式实现核心,发布/订阅提供扩展点
class Observable {
constructor() {
this._observers = new Set();
this._eventBus = new PubSub();
}
subscribe(observer) {
this._observers.add(observer);
}
emit(event, data) {
// 先通知直接观察者
this._observers.forEach(obs => obs.onEvent(event, data));
// 再通过事件总线广播
this._eventBus.publish(event, data);
}
on(event, callback) {
this._eventBus.subscribe(event, callback);
}
}
模式选择的决策因素
-
系统边界
- 模块内部通信:观察者模式
- 跨模块/跨应用通信:发布/订阅
-
生命周期管理
- 需要精细控制订阅关系时:观察者模式
- 需要松散耦合时:发布/订阅
-
性能需求
- 高频事件:观察者模式(直接调用效率更高)
- 低频事件:发布/订阅(解耦带来的灵活性更重要)
常见误区和陷阱
-
内存泄漏风险
- 观察者模式:忘记调用removeObserver()
- 发布/订阅:忘记取消订阅
-
过度使用发布/订阅
// 反模式:滥用事件总线导致"事件地狱" eventBus.on('A', () => { eventBus.emit('B'); }); eventBus.on('B', () => { eventBus.emit('C'); });
-
同步/异步混淆
- 观察者模式中误用异步通知
- 发布/订阅中错误假设消息顺序
现代JavaScript中的演进
-
Observable API
// RxJS示例 const observable = new Observable(subscriber => { subscriber.next(1); setTimeout(() => subscriber.next(2), 1000); }); observable.subscribe({ next: value => console.log(value) });
-
Proxy实现的自动观察
function createObservable(target) { const observers = new Set(); return new Proxy(target, { set(obj, prop, value) { obj[prop] = value; observers.forEach(obs => obs()); return true; } }); }
框架中的具体实现
-
React的setState
- 内部使用类似观察者模式的机制通知组件更新
- 但通过调度器实现了类似发布/订阅的批处理能力
-
Vue的响应式系统
- 每个响应式属性都是独立的Dep(Subject)
- Watcher作为Observer,通过闭包保持对Dep的引用
-
Node.js的EventEmitter
const EventEmitter = require('events'); const emitter = new EventEmitter(); emitter.on('event', () => console.log('Triggered')); emitter.emit('event');
下一篇: 状态模式(State)的对象行为变化管理