JavaScript设计模式「基于ES2024」:其他模式-发布订阅模式

144 阅读2分钟

发布订阅模式是一种消息范式,其中消息的发送者(称为发布者)不会将消息直接发送给特定的接收者(称为订阅者)。相反,发布者将消息分类,而不需要知道哪些订阅者(如果有的话)可能存在。同样,订阅者表示对一个或多个类别的兴趣,只接收感兴趣的消息,而不需要知道哪些发布者(如果有的话)存在。

// 事件管理器类
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

    • 可以订阅特定类别的新闻。
    • 维护一个取消订阅函数的数组,允许一次性取消所有订阅。

优点

  • 解耦:发布者和订阅者之间是松耦合的,它们不需要知道对方的存在。
  • 可扩展性:可以轻松添加新的发布者和订阅者,而不影响现有代码。
  • 动态关系:可以在运行时动态地建立订阅关系。
  • 一对多通信:一个发布者可以同时通知多个订阅者。