js设计模式系列篇:发布-订阅者模式

505 阅读2分钟

与观察者(observer)模式的不同

观察者模式要求希望接受到通知的观察者必须订阅内容改变的事件。

而发布订阅者模式使用了一个主题/事件通道。该通道介于事件的发布者和希望接受该事件通知的订阅者之间。该模式允许我们自定义事件,事件也可以传递自定义参数,参数是需要通知给订阅者的数据。其目的是避免发布者和订阅者之间产生依赖关系。

image.png

与观察者模式不同,发布订阅者模式允许任何订阅者注册和接受发布者发出的通知,而无需依附与某个目标对象上。

实现

const pubsub = {};

(function (q) {
    const topics = []
    let subId = -1

    q.publish = function (topic, args) {
        //该事件没有订阅者直接返回
        if (!topics[topic]) {
            return false
        }
        //获取订阅者
        const subscribers = topics[topic]
        let len = subscribers ? subscribers.length : 0
        //遍历触发所有该事件订阅者的注册函数
        while (len--) {
            subscribers[len].func(topic, args)
        }
        return this
    }
    //订阅事件,只需要把将其加入到topics对象就可以
    q.subscribe = function (topic, func) {
        if (!topics[topic]) {
            topics[topic] = []
        }

        const token = (++subId).toString()

        topics[topic].push({
            token,
            func
        })
        return token
    }
})(pubsub)

上面的代码是一个立即执行函数,我们可以把pubsub看作是之前提到的主题/事件通道。发布者可以往这个通道发布事件,订阅者可以在这个通道订阅事件。具体的调用如下

//简单的消息记录其记录所有通过订阅者接收到的主题(topic)和数据
const messageLogger = function (topics, data) {
    console.log(`Logging${topics}:${data}`)
}

//订阅者监听订阅的topic,一旦该topic广播一个通知,订阅者就调用回调函数
const subscription = pubsub.subscribe("inbox/newMessage", messageLogger)
const subscription1 = pubsub.subscribe("inbox/newMessage", messageLogger)

//发布者负责发布程序感兴趣的topic或通知,例如

pubsub.publish("inbox/newMessage", "hello word")

执行以上代码,即可在控制台看到输出

image.png

我们可以看到,上面的代码实现中,并没有类似与观察者模式的具体的目标对象(Subject),也没有依附在目标对象上的观察者。所有的发布者和订阅者,都只是在主题/事件通道进行发布和订阅。发布者和订阅者并不关心彼此,这也是该模式与观察者模式的一个明显的区别。

具体的观察者模式的介绍,可以参考我上一篇文章: js设计模式系列篇:观察者模式

以上实例代码均来自 JavaScript设计模式 Addy Osmani著 徐涛译