发布订阅模式
发布订阅模式和观察者模式是两种常见的设计模式,用于处理事件和通信。在本文中,我们将逐步构建一个功能完善的EventEmitter,并通过这一过程来深入理解发布订阅模式。
简洁的版本
我们构建一个简单的发布订阅模型:
class EventEmitter {
constructor() {
this.events = {};
}
// 订阅事件
on(event, listener) {
if (!this.events[event]) {
this.events[event] = [];
}
this.events[event].push(listener);
}
// 卸载事件
off(event, listener) {
if (this.events[event]) {
this.events[event] = this.events[event].filter(l => l !== listener);
}
}
// 发布事件
emit(event, data) {
if (this.events[event]) {
this.events[event].forEach(listener => listener(data));
}
}
}
我们创建了一个EventEmitter
类,其中包括了on
、off
和emit
方法,分别用于订阅事件、卸载事件和发布事件。
代码流程解释:
-
on(event, listener)
: 该函数用于订阅事件。如果事件列表中不存在特定事件,我们会创建一个新的事件数组,然后将传入的监听器添加到该数组中。 -
off(event, listener)
: 这个函数用于卸载事件。我们首先检查事件列表是否包含特定事件,如果是,则使用filter
方法将传入的监听器从事件数组中移除。 -
emit(event, data)
: 该函数用于发布事件。如果事件列表包含特定事件,我们将遍历事件数组并触发每个监听器,传递事件数据。
升级版:添加一次性调用
我们引入了一次性订阅,也就是once
方法。
// ...
// 一次性订阅
once(event, listener) {
const onceWrapper = data => {
listener(data);
this.off(event, onceWrapper);
};
this.on(event, onceWrapper);
}
流程解释:
-
once(event, listener)
: 这个函数允许你一次性订阅事件,即事件触发一次后自动卸载订阅。它内部创建了一个包装函数onceWrapper
,该函数会在事件触发时执行一次性监听器,然后自动卸载订阅,以确保监听器只会执行一次。
进阶版:支持连续调用
让我们的EventEmitter
支持连续调用,这样我们可以在一个链中连续执行多个方法。
class EventEmitter {
// ...
// 订阅事件,支持连续调用
on(event, listener) {
if (!this.events[event]) {
this.events[event] = [];
}
this.events[event].push(listener);
return this;
}
// 卸载事件,支持连续调用
off(event, listener) {
if (this.events[event]) {
this.events[event] = this.events[event].filter(l => l !== listener);
}
return this;
}
// 一次性订阅,支持连续调用
once(event, listener) {
const onceWrapper = data => {
listener(data);
this.off(event, onceWrapper);
};
this.on(event, onceWrapper);
return this;
}
}
流程解释:
-
on(event, listener)
,off(event, listener)
,once(event, listener)
: 在这一步,我们添加了return this;
语句,这意味着每个方法调用后都会返回EventEmitter
实例。这允许我们在一个链式调用中连续调用不同的方法,从而提高了代码的可读性。
这三个函数的返回使得我们可以像下面这样连续调用它们:
eventEmitter
.on('event1', listener1)
.on('event2', listener2)
.once('event3', listener3)
.off('event4', listener4);
通过连续调用,我们可以在一个链中轻松执行多个操作。
完整版:添加事件缓存
在第四步中,我们引入了事件缓存,允许查看已经订阅的事件。
// ...
// 获取事件列表
getEvents() {
return Object.keys(this.events);
}
}
代码流程解释:
-
getEvents()
: 这个函数允许你获取已订阅事件的列表。它返回一个数组,包含了所有已经订阅的事件名称。
发布订阅模式与观察者模式区别
主要区别:
- 发布订阅模式更加灵活,允许多个发布者和订阅者之间的多对多关系,发布者不需要了解订阅者的存在。它通常使用一个中介来处理事件分发。
- 观察者模式通常是一对多关系,一个主题对象可以有多个观察者,但观察者的接口是明确定义的。主题对象需要维护观察者列表,并在状态变化时通知观察者。
观察者模式示例:
// 主题
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(message) {
this.observers.forEach(observer => observer.update(message));
}
}
// 观察者
class Observer {
update(message) {
console.log(`Received message: ${message}`);
}
}
const subject = new Subject();
const observer1 = new Observer();
const observer2 = new Observer();
subject.addObserver(observer1);
subject.addObserver(observer2);
subject.notify("Hello, observers!");
在我们上面的介绍中,发布订阅模式使用了中介者来处理事件的发布和订阅,而观察者模式是一对多的关系,主题对象直接通知观察者。
总结
通过逐步构建一个支持连续调用的事件模型,我们介绍了基本的发布订阅模式。同时也简单介绍了发布订阅模式和观察者模式之间差异。发布订阅模式更加灵活,支持多对多关系,适用于更多的场景,而观察者模式是一对多关系,明确定义了观察者的接口。最后希望本文对大家理解发布订阅模式有帮助。
网友评论