发布订阅模式和观察者模式的区别
首先,无论是观察者模式还是发布-订阅模式,都是为了在对象之间实现低耦合的通信。这两种模式都涉及到 "订阅者" 和 "发布者" 的概念。订阅者订阅某个事件或数据的更新,当发布者发布这个事件或更新这个数据时,所有的订阅者都会得到通知。
观察者模式:在观察者模式中,发布者和订阅者之间有直接的关联。换句话说,发布者直接调用订阅者的方法来通知他们。因此,发布者需要知道它的订阅者是谁。例如,我们可以想象一个报纸订阅的情形,报纸公司(发布者)有一个明确的订阅者列表,当有新的报纸出版时,它会直接将报纸送到订阅者的门前。
class Publisher {
constructor() {
this.observers = [];
}
subscribe(observer) {
this.observers.push(observer);
}
publish() {
this.observers.forEach((observer) => {
observer.notify();
});
}
}
class Observer {
notify() {
console.log('Observer has been notified.');
}
}
const publisher = new Publisher();
const observer = new Observer();
publisher.subscribe(observer);
publisher.publish(); // Log: Observer has been notified.
发布-订阅模式:与观察者模式不同,发布-订阅模式中的发布者和订阅者之间不存在直接关联。而是通过一个 "消息中心" 或 "事件总线" 来进行通信的。发布者发布事件到消息中心,订阅者则从消息中心订阅事件。因此,发布者并不需要知道订阅者是谁。我们可以想象电视广播的情形,电视台(发布者)只负责将节目(事件)发送到电视信号中(消息中心),而作为观众(订阅者),你只需要在电视上调到正确的频道(订阅事件)就可以看到节目。
class EventBus {
constructor() {
this.listeners = {};
}
subscribe(eventName, listener) {
if (!this.listeners[eventName]) {
this.listeners[eventName] = [];
}
this.listeners[eventName].push(listener);
}
publish(eventName) {
if (this.listeners[eventName]) {
this.listeners[eventName].forEach((listener) => {
listener();
});
}
}
}
const eventBus = new EventBus();
eventBus.subscribe('news', () => console.log('Subscriber has been notified.'));
eventBus.publish('news'); // Log: Subscriber has been notified.
- 在观察者模式中,发布者和订阅者直接进行交互,而在发布-订阅模式中,发布者和订阅者通过一个第三方(消息中心/事件总线)进行交互。
- 观察者模式中的发布者知道订阅者是谁,而发布-订阅模式中的发布者并不知道订阅者是谁。
总结
从理论上讲,两种模式都可以实现实时通知。无论是观察者模式还是发布-订阅模式,一旦有事件发生或数据变化,订阅者都可以立即收到通知。
区别主要在于这两种模式通知订阅者的方式不同:
- 在观察者模式中,发布者直接通知订阅者。因此,只要发布者发出通知,订阅者就可以立即收到。
- 在发布-订阅模式中,发布者将通知发送到一个中间代理(通常被称为事件总线或消息中心),然后由这个中间代理将通知传递给订阅者。这意味着,尽管可能存在一点延迟(因为通知需要首先到达中间代理,然后再从中间代理传递给订阅者),但这个延迟通常可以忽略不计,所以订阅者依然可以近乎实时地收到通知。
实际上,发布-订阅模式能够提供更多的灵活性,因为它解耦了发布者和订阅者。由于发布者和订阅者都只与事件总线交互,而不是直接交互,因此可以更容易地添加、删除或修改发布者和订阅者,而无需改变其他部分的代码。这使得发布-订阅模式特别适合大型、复杂的系统,其中的组件可能需要在运行时动态地添加或删除。
同时,通过使用事件总线,发布-订阅模式还可以实现更复杂的消息传递模式,例如消息过滤、消息持久化、消息排序等,这是观察者模式无法直接实现的。