发布/订阅模式和观察者模式的区别
有人说发布/订阅模式就是观察者模式,有人说发布/订阅模式是观察者的变种。不管怎样,必须承认他们之间是有区别的。
其中最主要的区别就是在发布/订阅模式中多了一层主题/事件通道。
回想在观察者模式中,主题和观察者是直接发生关系的,观察者需要注册到主题中去。而在发布/订阅模式中,发布者和订阅者并不知道彼此的存在,他们都直接和中间层(主题/事件通道)发生关系。
在这里插入图片描述
我们还是拿餐馆吃饭来举例。
在观察者模式中,我们把餐馆(目标)看成一个整体,和客户(观察者)直接发生关系。
在发布/订阅模式中,我们把餐馆分成两部分,后厨(发布者)和前台(主题/事件通道)。而前来吃饭的顾客就是订阅者。
现在有顾客进来吃饭,会先去前台点餐。也就是订阅过程。顾客也不会直接跑到后厨去点餐。
后厨做好的餐,会通知前台。这也就是发布过程。
前台会把做好的餐,端给对应的顾客。这就是触发过程。
实现
下面我们来实现一个简单的发布/订阅模式:
var pubsub = {}
;(function(q) {
var topics = {}
var subUid = -1
// 发布或广播事件,包含特定的 topic 和参数。
q.publish = function(topic, args) {
if (!topics[topic]) {
return false
}
var subscribes = topics[topic]
var len = subscribes ? subscribes.length : 0
while (len--) {
subscribes[len].func(topic, args)
}
return this
}
// 订阅事件,通过特定的 topic 和 回调函数。在 topic/evnet 触发时会执行回调。
q.subscribe = function(topic, func) {
if (!topics[topic]) {
topics[topic] = []
}
var token = (++subUid).toString()
topics[topic].push({
token: token,
func: func
})
return token
}
// 取消订阅。通过订阅时生成的引用标记。
q.unsubscribe = function(token) {
for (var m in topics) {
if (topics[m]) {
for (var i = 0, j = topics[m].length; i < j; i++) {
if (topics[m][i].token === token) {
topics[m].splice(i, 1)
return token
}
}
}
}
}
return this
}
)(pubsub)
使用
用代码的方式来模仿下餐馆点餐的过程。
// A 顾客来餐馆点了一份牛肉拉面,不要辣。(订阅过程)
var food1 = pubsub.subscribe('beef ramen', function(topic, args) {
if (args === 'not spicy') {
console.log(topic + ' ' + args)
}
})
// B 顾客来餐馆也点了一份牛肉拉面,正常做。(订阅过程)
var food2 = pubsub.subscribe('beef ramen', function(topic, args) {
if (args === 'normal') {
console.log(topic + ' ' + args)
}
})
// 后厨做好一份牛肉拉面,通知前台。
// 前台通知所有点牛肉拉面的顾客,并告诉顾客这碗面没放辣椒。
// 顾客根据自己的需求去拿餐。(发布触发过程)
// 打印 'beef ramen not spicy'
pubsub.publish('beef ramen', 'not spicy')
// A 顾客按自己需求拿到饭,开始吃饭。(取消订阅)
pubsub.unsubscribe(food1)
// 后厨做好一份牛肉拉面,通知前台。
// 前台通知所有点牛肉拉面的顾客,并告诉顾客这碗面正常做。
// 顾客根据自己的需求去拿餐。(发布触发过程)
// // 打印 'beef ramen normal'
pubsub.publish('beef ramen', 'normal')
// B 顾客按自己需求拿到饭,开始吃饭。(取消订阅)
pubsub.unsubscribe(food2)
// 由于 A、B 顾客都已经吃到了拉面,并取消了订阅。
// 已经没有吃拉面的顾客了。
// 这个时候,前台再通知拉面做好了,就没有人消费了。
pubsub.publish('beef ramen', 'normal')
参考
《JavaScript 设计模式》 —— Addy Osmani
作者:userkang
来源:CSDN
原文:https://blog.csdn.net/userkang/article/details/87192128
版权声明:本文为博主原创文章,转载请附上博文链接!