什么是发布订阅模式
发布-订阅模式又叫做观察者模式。他定义了一种一对多的依赖关系,即当一个对象的状态发生变化的时候,所有依赖的对象都会得到通知。
基本实现
把自己想象成公司的秘书,领导需要开一个会议,需要你去通知所有参会人员;
这里有几个元素:
- 发布者(秘书)
- 与会人员(需要参加会议的人员)
还有几个方法:
- 制作与会人员名单
- 删除不参加的人
- 发送会议通知,以及必要的时间地点等信息
你首先需要把所有的与会人员名单收集起来(也就是添加依赖函数),然后根据清单发送通知,并携带必要的会议信息,与会人员收到信息后会做出相应的反应(回调函数中的处理逻辑)
let yourMsg = {};
yourMsg.peopleList = [];
yourMsg.listen = function (fn) {
this.peopleList.push(fn);
}
yourMsg.triger = function () {
let that = this
let args = arguments
setTimeout(function() {
for(let i = 0,fn;fn=that.peopleList[i++];){
fn.apply(that,args);
}
},2000)
}
yourMsg.listen(function () {
console.log('我被通知了,我回去参加会议,给我时间地点')
console.log(`title:${arguments[0]}`);
console.log(`time:${arguments[1]}`);
})
yourMsg.listen(function () {
console.log('我被通知了,但是我不care')
})
yourMsg.listen(function () {
console.log('我被通知了,Jack代替我去')
})
yourMsg.triger('会议', '3:00');
复制代码
这便是一个简单的订阅发布模式
分主题的订阅发布模式
随着公司业务量的增加,你的会议不需要所有人都参加,你需要分主题维护一份参会人员名单;
比如,产品会议需要小A,小B参加;而技术会议需要小C,小D参加;
这里我们通过在添加与会人员的时候添加一个‘key’值代表该成员需要参加的会议类型;
let yourMsg = {};
yourMsg.peopleList = [];
yourMsg.listen = function (key, fn) {
if (!this.peopleList[key]) {
this.peopleList[key] = [] // 如果分类中没有此类型的消息,则新建一个
}
this.peopleList[key].push(fn);
}
yourMsg.triger = function () {
let key = Array.prototype.shift.call(arguments)
let fns = this.peopleList[key]
let that = this
let args = arguments
if (!fns) {
return false
}
setTimeout(function() {
for(let i = 0,fn;fn=fns[i++];){
fn.apply(that,args);
}
}, 200)
}
yourMsg.listen('prod', function () {
console.log('小A: 我只关心会议消息,其它的事别来烦我')
console.log(`title:${arguments[0]}`);
console.log(`time:${arguments[1]}`);
})
yourMsg.listen('prod', function () {
console.log('小B: 会议使我精神百倍!')
console.log(`title:${arguments[0]}`);
console.log(`time:${arguments[1]}`);
})
yourMsg.listen('tech', function () {
console.log('小C:我只关心技术会议,产品会议让那些傻瓜去吧')
})
yourMsg.listen('tech', function () {
console.log('小D:又到技术会议时间了?我是被逼的!')
})
yourMsg.triger('prod', '开会啦', '下午3:00');
yourMsg.triger('tech');
复制代码
可以随处调用的订阅发布功能
由于你们公司效益特别好,你一个秘书已经忙不过来,这个时候,公司又来了一位秘书。你只需要把你通知会议安排的方法告诉他,让他按照这个模式去做就好了。
这里我们并没有用对象来做,而是通过Object.Create()通过依赖关系来实现类似继承的效果;更加的合理简洁;个人我认为在js中完全没必要也不应该使用面向对象的模式,因为它压根就么有所谓的面向对象,相比于东施效颦不如发挥自己的特点。
let installer = {
peopleList: [],
listen (key, fn) {
if (!this.peopleList[key]) {
this.peopleList[key] = [] // 如果分类中没有此类型的消息,则新建一个
}
this.peopleList[key].push(fn);
},
triger () {
let key = Array.prototype.shift.call(arguments)
let fns = this.peopleList[key]
let that = this
let args = arguments
if (!fns) {
return false
}
setTimeout(function() {
for(let i = 0,fn;fn=fns[i++];){
fn.apply(that,args);
}
}, 200)
},
remove (key, fn) {
let fns = this.peopleList[key]
if (!fns) {
return false
}
for (let index = 0; index < fns.length; index++) {
const _fn = fns[index];
if(_fn === fn){
fns.splice(index,1);
}
}
}
}
let yourMsg = Object.create(installer);
yourMsg.listen('prod', function () {
console.log('小A: 我只关心会议消息,其它的事别来烦我')
console.log(`title:${arguments[0]}`);
console.log(`time:${arguments[1]}`);
})
yourMsg.listen('prod', function () {
console.log('小B: 会议使我精神百倍!')
console.log(`title:${arguments[0]}`);
console.log(`time:${arguments[1]}`);
})
let smA = function () {
console.log('小C:我只关心技术会议,产品开会让那些傻瓜去吧')
}
let smB = function () {
console.log('小D:又到技术会议时间了?我是被逼的!')
}
yourMsg.listen('tech', smA)
yourMsg.listen('tech', smB)
yourMsg.remove('tech', smA)
yourMsg.triger('prod', '开会啦', '下午3:00');
yourMsg.triger('tech', smA);
复制代码