20190411期
设计模式-如何理解观察者(发布订阅)模式?
定义: 观察者模式又叫发布订阅模式(Publish/Subscribe),它定义了一种一对多的关系,让多个观察者对象同时监听某一个主题对象,这个主题对象的状态发生变化时就会通知所有的观察者对象,使得它们能够自动更新自己
生活实例理解:你今天去看一个楼盘,去了之后发布楼盘还没有对外销售,你不知道楼盘时候会对外销售,于是你找了楼盘的负责人,告诉他什么时候楼盘开始销售了电话通知你,然后想要买的人不是你一个,其它人也是通过留电话的方式给销售负责人来及时获取消息
不难发现,上面的例子正好对应上我们的观察者模式的定义, 多个想要买房的人同时订阅了一个主题(楼盘对外销售),这个主题更新时,这些观察者(买房)都会作出相应的动作
最熟悉的代码理解:
实际上,我们经常用到的事件绑定就是发布订阅模式
在这里我们想在用户点击的时候做出相应的处理,但是我们不知道用户在什么时候去点击,所以我们去订阅body上的click事件,在这里我们还可以去随意增加订阅者,这样并不影响我们的发布者
document.body.addEventListener('click',function(){
console.log('JS 每日一题')
},false)
document.body.addEventListener('click',function(){
console.log('今天你打卡了吗?')
},false)
实现一个简版的观察者
首先我们顺一下思路
- 谁是发布者
- 谁是订阅者
- 发生改变时怎么通知订阅者作出相应动作
const Boss = {} //楼盘销售负责人
Boss.clientList = []; // 存放订阅者的回调
Boss.listen = function(fn){ // 增加订阅者
this.clientList.push(fn); // 将买房人的号码缓存起来
}
Boss.trigger = function(){ // 发布消息
for(var i=0,fn; fn= this.clientList[i++];){
fn.apply(this,arguments)
}
}
Boss.listen(function(msg){
console.log(msg) // 开始销售了
})
cdBoss.trigger('开始销售了') // 发布消息
我们已经实现在最简易版的发布订阅,但其实是存在问题的,每个人可能订阅户型是不同的, 上面我们实现的是,只要一开始销售就通知所有订阅的人,显然是不合理的,我们将代码再来改写一下
// 订阅时给其加一个key做为标识,就相当于key就是订阅者的身份
Boss.listen = function(key, fn){
if (!this.clientList[key]) {
// 如果没有此类订阅,就给该类订阅增加一个缓存列表
this.clientList[key] = []
}
this.clientList[key].push(fn);
}
Boss.trigger = function(){ // 发布消息
const key = Array.prototype.shift.call(arguments),
// 取出消息类型
fns = this.clientList[key]
if (!fns || !fns.length) return // 如果该类消息没有订阅直接返回
for (var i = 0, fn; fn = fns[i++]) {
fn.apply(this, arguments)
}
}
Boss.listen('老王', function (msg) {
console.log('老王订阅户型' + cdName)
})
Boss.listen('老李', function (cdName) {
console.log('老李订阅户型' + cdName)
})
Boss.trigger('老王', '143平米');
Boss.trigger('老李', '888平米');
好了,经过改写,消息只会推送给相关的订阅者了
总结
- 时间上的解藕
- 对象之间的解藕
总的来说,观察者模式所做的工作就是在解耦,让耦合的双方都依赖于抽象,而不是依赖于具体。从而使得各自的变化都不会影响到另一边的变化
关于JS每日一题
JS每日一题可以看成是一个语音答题社区
每天利用碎片时间采用60秒内的语音形式来完成当天的考题
群主在次日0点推送当天的参考答案
- 注 绝不仅限于完成当天任务,更多是查漏补缺,学习群内其它同学优秀的答题思路
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。