与观察者(observer)模式的不同
观察者模式要求希望接受到通知的观察者必须订阅内容改变的事件。
而发布订阅者模式使用了一个主题/事件通道。该通道介于事件的发布者和希望接受该事件通知的订阅者之间。该模式允许我们自定义事件,事件也可以传递自定义参数,参数是需要通知给订阅者的数据。其目的是避免发布者和订阅者之间产生依赖关系。
与观察者模式不同,发布订阅者模式允许任何订阅者注册和接受发布者发出的通知,而无需依附与某个目标对象上。
实现
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")
执行以上代码,即可在控制台看到输出
我们可以看到,上面的代码实现中,并没有类似与观察者模式的具体的目标对象(Subject),也没有依附在目标对象上的观察者。所有的发布者和订阅者,都只是在主题/事件通道进行发布和订阅。发布者和订阅者并不关心彼此,这也是该模式与观察者模式的一个明显的区别。
具体的观察者模式的介绍,可以参考我上一篇文章: js设计模式系列篇:观察者模式
以上实例代码均来自 JavaScript设计模式 Addy Osmani著 徐涛译