观察者模式与发布订阅模式的区别
Author:zhoulujun Date:
观察者模式和发布-订阅模式在细节和实现上,这两种模式有着明显的不同。
观察者模式(Observer Pattern)
在观察者模式中,一个或多个观察者对主题(Subject)的状态感兴趣,并在主题状态改变时接收通知。
+------------+ +------------+
| 观察者 |<--->| 目标 |
+------------+ +------------+
观察者需要显式地注册(订阅)到主题上,以便当主题状态发生变化时,主题直接调用观察者提供的方法来通知它们。这种模式通常在观察者(Observers)和被观察目标(Subject)之间建立较紧密的耦合关系。
主题(Subject)直接持有观察者(Observer)的引用。
对象之间的通信是直接进行的。
发布-订阅模式(Publish-Subscribe Pattern)
发布-订阅模式在观察者模式的基础上引入了一个消息代理(也称为事件通道或消息队列)的概念,以实现发布者和订阅者之间的解耦。
+------------+ +------------+ +------------+
| 观察者 |<--->| 事件调度中心 |<--->| 目标 |
+------------+ +------------+ +------------+
在这个模式中,发布者将事件发布到一个中间的消息通道,而订阅者从该消息通道订阅事件。这样,发布者和订阅者不需要了解对方的存在,只与消息通道进行交互,从而实现了更高的系统解耦。
发布者和订阅者之间通过消息代理进行间接通信。
提高了系统各部分的独立性和可替换性。
发布-订阅好比观察者模式增加了一个中介者,发布者和订阅着只和中介者打交到。中介者持有发布者和订阅者都引用或传入的回调,用来做订阅和通知。
观察者模式与发布-订阅模式在事件驱动方式中的应用
观察者模式
直接通信:主题(Subject)直接管理一组观察者(Observer),并在状态发生变化时直接通知它们。这意味着观察者需要直接注册(或订阅)到主题对象上。
紧耦合:观察者必须实现一个特定的接口(这里是update方法),以便主题能够通过这个接口通知到每个观察者。这导致观察者与主题之间的耦合度相对较高,因为观察者必须知道主题并按照主题的要求实现具体接口。
实现简单:由于观察者直接注册到主题上,实现这一模式相对直接和简单,适用于对象间关系较为固定,通信路径明确的场景。
发布-订阅模式
间接通信:介于发布者(Publisher)和订阅者(Subscriber)之间的通信是通过一个中间件(在上述示例中为PubSub类)来进行的,该中间件维护了事件(或主题)及其相应回调函数的映射。这个机制使得发布者和订阅者之间的关联变得更加灵活和松散。
低耦合:订阅者不需要知道谁是发布者,它只需要知道从哪里订阅感兴趣的事件。同样,发布者不需要知道谁订阅了事件,它只需要将事件发布到中间件。中间件负责将事件通知给所有相关的订阅者。
复杂实现:由于加入了中间件的概念,实现发布-订阅模式相比观察者模式在逻辑上稍微复杂一些。但是这种复杂性带来的好处是更高的灵活性和低耦合度,适用于大型应用和分布式系统。
观察者模式中主体和观察者是互相感知的,发布-订阅模式是借助第三方来实现调度的,发布者和订阅者之间互不感知
应用场景需要较紧密的对象之间互动,并且这些对象之间的关系比较固定,那么观察者模式可能是较好的选择。
系统需要高度的解耦,以及支持大规模的、分布式的交互,那么发布-订阅模式可能更加合适,因为它通过消息代理来减少系统组件之间的直接依赖。总的来说,发布-订阅模式适合更复杂的场景。
在「一对多」的场景下,发布者的某次更新只想通知它的部分订阅者?
在「多对一」或者「多对多」场景下。一个订阅者依赖于多个发布者,某个发布者更新后是否需要通知订阅者?还是等所有发布者都更新完毕再通知订阅者?
观察者模式和订阅-发布模式代码实现
观察者模式
class Subject { constructor() { // 主题(Subject)直接管理一组观察者(Observer) this.observers = []; // 观察者列表 } subscribe(observer) { this.observers.push(observer); } unsubscribe(observer) { this.observers = this.observers.filter(obs => obs !== observer); } notify(data) { // 在状态发生变化时直接通知它们。这意味着观察者需要直接注册(或订阅)到主题对象上。 this.observers.forEach(observer => observer.update(data)); } } class Observer { constructor(name) { this.name = name; } //观察者必须实现一个特定的接口(这里是update方法),以便主题能够通过这个接口通知到每个观察者。这导致观察者与主题之间的耦合度相对较高,因为观察者必须知道主题并按照主题的要求实现具体接口。 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.subscribe(observer1); subject.subscribe(observer2); subject.notify("Hello World!");
上面的代码定义了一个主题(Subject)类和一个观察者(Observer)类。主题类管理一组观察者并提供通知方法,观察者类则具有更新方法以响应通知。
发布-订阅模式
发布-订阅模式引入了一个中间层(通常被称为“事件通道”或“Broker”),这使得发布者和订阅者之间的联系更加松散。
class PubSub { constructor() { this.subscribers = {}; } subscribe(event, callback) { if (!this.subscribers[event]) { this.subscribers[event] = []; } this.subscribers[event].push(callback); } unsubscribe(event, callback) { this.subscribers[event] = this.subscribers[event].filter(cb => cb !== callback); } publish(event, data) { if (this.subscribers[event]) { this.subscribers[event].forEach(callback => callback(data)); } } } // 使用 const pubSub = new PubSub(); const callback1 = data => console.log(`Callback 1 received data: ${data}`); const callback2 = data => console.log(`Callback 2 received data: ${data}`); pubSub.subscribe('anEvent', callback1); pubSub.subscribe('anEvent', callback2); pubSub.publish('anEvent', 'Hello world!');
最典型的就是 Vue事件总线(Event Bus),具体参看《原生js实现事件监听类on/emit/off方法,Vue event事件机制解读》
最后,题外话:
观察者模式(Observer Pattern)属于23种设计模式之一。它是一种对象行为模式,用于定义对象间的一种一对多的依赖关系。
发布订阅模式(Publish-Subscribe Pattern)虽然与观察者模式有一定的相似性,但它并不是23种设计模式之一。发布订阅模式是一种消息传递模式,用于解耦发布者和订阅者。在发布订阅模式中,发布者发布消息,订阅者订阅消息,然后代理服务器负责将消息分发给订阅者。这种模式适用于实现事件处理系统,但并不限定于观察者模式。
转载本站文章《观察者模式与发布订阅模式的区别》,
请注明出处:https://www.zhoulujun.cn/html/theory/engineering/model/9072.html