JS订阅发布模式

667 阅读3分钟

什么是发布订阅模式

发布-订阅模式又叫做观察者模式。他定义了一种一对多的依赖关系,即当一个对象的状态发生变化的时候,所有依赖的对象都会得到通知。

基本实现

把自己想象成公司的秘书,领导需要开一个会议,需要你去通知所有参会人员;

这里有几个元素:

  • 发布者(秘书)
  • 与会人员(需要参加会议的人员)

还有几个方法:

  • 制作与会人员名单
  • 删除不参加的人
  • 发送会议通知,以及必要的时间地点等信息

你首先需要把所有的与会人员名单收集起来(也就是添加依赖函数),然后根据清单发送通知,并携带必要的会议信息,与会人员收到信息后会做出相应的反应(回调函数中的处理逻辑)

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);