发布/订阅模式(Publish/Subscribe)和观察者模式(Observer Pattern)在概念上非常相似,都是用于实现对象之间的松耦合通信。尽管它们在实现细节和使用场景上有所不同,但核心思想是相通的。
观察者模式
- 直接通信:在观察者模式中,观察者(Observer)直接订阅主题(Subject)。当主题状态改变时,会直接通知所有订阅的观察者。
- 紧密耦合:观察者需要直接注册到主题上,这意味着观察者和主题之间存在较紧密的耦合。
- 实现方式:通常由主题维护一个观察者列表,当主题状态改变时,遍历这个列表,逐个调用观察者的更新方法。
简单观察者模式示例:
class Subject {
constructor() {
this.observers = []; // 观察者列表
}
// 添加观察者
addObserver(observer) {
this.observers.push(observer);
}
// 移除观察者
removeObserver(observer) {
const index = this.observers.indexOf(observer);
if (index > -1) {
this.observers.splice(index, 1);
}
}
// 通知所有观察者
notify(data) {
this.observers.forEach(observer => observer.update(data));
}
}
class Observer {
constructor(name) {
this.name = name;
}
// 接收到主题的通知时调用
update(data) {
console.log(`${this.name} received data: ${data}`);
}
}
// 使用示例
const subject = new Subject();
const observer1 = new Observer('Observer 1');
const observer2 = new Observer('Observer 2');
subject.addObserver(observer1);
subject.addObserver(observer2);
subject.notify('Hello World!'); // 通知所有观察者
subject.removeObserver(observer2);
subject.notify('Second message'); // 只有 observer1 会接收到这个通知
在这个示例中,Subject 类维护了一个观察者列表,提供了添加观察者、移除观察者和通知观察者的方法。Observer 类定义了观察者应有的update 方法,用于接收来自主题的通知。通过创建 Subject 和 Observer 的实例并调用相应的方法,我们演示了观察者模式的工作流程。
发布/订阅模式
- 间接通信:发布/订阅模式通过一个称为“消息代理”或“事件总线”的第三方组件来管理事件和订阅者之间的通信,发布者发布事件到事件总线,而订阅者从事件总线订阅事件,发布者和订阅者不需要知道对方的存在。
- 松耦合:由于发布者和订阅者不直接交互,因此它们之间的耦合度更低,这使得系统的各个部分可以更独立地发展和变化。
- 实现方式:通常依赖于一个中间件或服务来传递消息,这个中间件负责存储订阅者信息和转发发布者的消息。
class EventBus {
constructor() {
this.events = {}; // 存储事件及其监听函数
}
// 订阅事件
subscribe(eventName, callback) {
if (!this.events[eventName]) {
this.events[eventName] = []; // 如果事件不存在,则初始化为空数组
}
this.events[eventName].push(callback);
}
// 发布事件
publish(eventName, data) {
const callbacks = this.events[eventName];
if (callbacks) {
callbacks.forEach(callback => callback(data)); // 触发所有订阅了该事件的回调函数
}
}
// 取消订阅
unsubscribe(eventName, callback) {
const callbacks = this.events[eventName];
if (callbacks) {
const index = callbacks.indexOf(callback);
if (index > -1) {
callbacks.splice(index, 1); // 移除指定的回调函数
}
}
}
}
// 使用示例
const eventBus = new EventBus();
// 订阅事件
eventBus.subscribe('sayHello', name => console.log(`Hello ${name}!`));
eventBus.subscribe('sayGoodbye', name => console.log(`Goodbye ${name}!`));
// 发布事件
eventBus.publish('sayHello', 'Alice');
eventBus.publish('sayGoodbye', 'Bob');
// 取消订阅
const goodbyeCallback = name => console.log(`Goodbye ${name}!`);
eventBus.unsubscribe('sayGoodbye', goodbyeCallback);
// 再次发布事件,验证取消订阅是否成功
eventBus.publish('sayGoodbye', 'Charlie');
在这个示例中,EventBus 类提供了subscribe、publish 和 unsubscribe方法,分别用于订阅事件、发布事件和取消订阅事件。通过创建EventBus 的实例并调用相应的方法,我们演示了发布/订阅模式的工作流程。
使用场景
- 观察者模式:通常用于实现对象之间的一对多依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都将得到通知。适用于对象之间的关系比较明确,且数量较少的情况。
- 发布/订阅模式:适用于大型应用和系统,特别是在分布式系统中,用于解耦应用组件。由于其低耦合特性,它允许系统的不同部分独立地进行扩展和维护。常见于事件驱动的架构和消息队列系统中。
实际应用示例
- 观察者模式:GUI 应用中的事件监听(如按钮点击)、模型-视图-控制器(MVC)模式中模型(Model)和视图(View)之间的关系。
- 发布/订阅模式:消息队列系统(如 RabbitMQ、Kafka)、服务间的异步通信、Web 应用中的事件总线系统。