发布订阅模式是一种消息范式,其中消息的发送者(称为发布者)不会将消息直接发送给特定的接收者(称为订阅者)。相反,发布者将消息分类,而不需要知道哪些订阅者(如果有的话)可能存在。同样,订阅者表示对一个或多个类别的兴趣,只接收感兴趣的消息,而不需要知道哪些发布者(如果有的话)存在。
// 事件管理器类
class EventEmitter {
#events = new Map();
// 订阅事件
subscribe(eventName, callback) {
if (!this.#events.has(eventName)) {
this.#events.set(eventName, []);
}
this.#events.get(eventName).push(callback);
// 返回取消订阅的函数
return () => {
const callbacks = this.#events.get(eventName);
const index = callbacks.indexOf(callback);
if (index !== -1) {
callbacks.splice(index, 1);
}
};
}
// 发布事件
publish(eventName, data) {
if (this.#events.has(eventName)) {
this.#events.get(eventName).forEach(callback => callback(data));
}
}
}
// 新闻发布者类
class NewsPublisher {
#emitter;
constructor(emitter) {
this.#emitter = emitter;
}
publishNews(category, news) {
console.log(`Publishing ${category} news: ${news}`);
this.#emitter.publish(category, news);
}
}
// 订阅者类
class NewsSubscriber {
#name;
#emitter;
#unsubscribes = [];
constructor(name, emitter) {
this.#name = name;
this.#emitter = emitter;
}
subscribe(category) {
console.log(`${this.#name} subscribed to ${category} news`);
const unsubscribe = this.#emitter.subscribe(category, (news) => {
console.log(`${this.#name} received ${category} news: ${news}`);
});
this.#unsubscribes.push(unsubscribe);
}
unsubscribeAll() {
console.log(`${this.#name} unsubscribed from all news`);
this.#unsubscribes.forEach(unsubscribe => unsubscribe());
this.#unsubscribes = [];
}
}
// 使用示例
function demonstratePublishSubscribe() {
const eventEmitter = new EventEmitter();
const publisher = new NewsPublisher(eventEmitter);
const subscriber1 = new NewsSubscriber("Alice", eventEmitter);
const subscriber2 = new NewsSubscriber("Bob", eventEmitter);
const subscriber3 = new NewsSubscriber("Charlie", eventEmitter);
subscriber1.subscribe("sports");
subscriber1.subscribe("technology");
subscriber2.subscribe("sports");
subscriber3.subscribe("technology");
publisher.publishNews("sports", "Local team wins championship!");
publisher.publishNews("technology", "New AI breakthrough announced");
publisher.publishNews("politics", "Election results are in"); // No one subscribed to this
subscriber1.unsubscribeAll();
publisher.publishNews("sports", "World cup finals next week");
publisher.publishNews("technology", "Revolutionary new gadget released");
}
demonstratePublishSubscribe();
实现思路
-
EventEmitter类:- 使用私有字段
#events(Map)来存储事件及其回调函数。 subscribe方法允许订阅事件,并返回一个取消订阅的函数。publish方法用于发布事件,触发所有相关的回调函数。
- 使用私有字段
-
NewsPublisher类:- 使用
EventEmitter来发布新闻。
- 使用
-
NewsSubscriber类:- 可以订阅特定类别的新闻。
- 维护一个取消订阅函数的数组,允许一次性取消所有订阅。
优点
- 解耦:发布者和订阅者之间是松耦合的,它们不需要知道对方的存在。
- 可扩展性:可以轻松添加新的发布者和订阅者,而不影响现有代码。
- 动态关系:可以在运行时动态地建立订阅关系。
- 一对多通信:一个发布者可以同时通知多个订阅者。