发布订阅模式与观察者模式

一、概念

观察者模式(Observer Pattern):观察者模式定义了对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都将得到通知,并自动更新。观察者模式属于行为型模式,行为型模式关注的是对象之间的通讯,观察者模式就是观察者和被观察者之间的通讯。

观察者模式有一个别名叫“发布-订阅模式”

发布订阅模式(Pub-Sub Pattern):经过时间的沉淀,已经独立于观察者模式,成为另外一种不同的设计模式。

称为发布者的消息发送者不会将消息直接发送给订阅者,这意味着发布者和订阅者不知道彼此的存在。在发布者和订阅者之间存在第三个组件,称为消息代理或调度中心或中间件,它维持着发布者和订阅者之间的联系,过滤所有发布者传入的消息并相应地分发它们给订阅者。

二、实现结构

发布订阅模式与观察者模式

三、代码实现

观察者模式

 //有一家猎人工会,其中每个猎人都具有发布任务(publish),订阅任务(subscribe)的功能
  //他们都有一个订阅列表来记录谁订阅了自己

  //定义一个猎人类,包括姓名,级别,订阅列表
  function Hunter(name, level){
    this.name = name;
    this.level = level;
    this.list = [];
  }

  //发布任务的功能
  Hunter.prototype.publish= function (parameters){
      var money = parameters.money;
      console.log(this.level + '猎人' + this.name + '寻求帮助')
      this.list.forEach(function (item, index){
              item(money)
          })
    };

  //订阅任务(subscribe)的功能
  Hunter.prototype.subscribe = function (targrt, fn){
    console.log(this.level + '猎人' + this.name + '订阅了' + targrt.name)
      targrt.list.push(fn)
    }

  //猎人工会的几个猎人

  let hunterMing = new Hunter('小明', '黄金')
  let hunterJin = new Hunter('小金', '白银')
  let hunterZhang = new Hunter('小张', '黄金')
  let hunterPeter = new Hunter('Peter', '青铜')

  //Peter等级较低,可能需要帮助,所以小明,小金,小张都订阅了Peter
  hunterMing.subscribe(hunterPeter, function (money){
        console.log('小明表示:' + (money > 200) ? '' : '暂时很忙,不能'+ '给予帮助')
      })
  hunterJin.subscribe(hunterPeter, function (){
        console.log('小金表示:给予帮助')
      })

  hunterZhang.subscribe(hunterPeter, function (){
        console.log('小张表示:给予帮助')
      })

  //Peter遇到困难,赏金198寻求帮助
  hunterPeter.publish({money: 198})
  //猎人们(观察者)关联他们感兴趣的猎人(目标对象),如Peter,当Peter有困难时,会自动通知给他们(观察者)

分析:

item(money)

主要是这个函数,来进行通知订阅者。

发布-订阅模式

  //主要功能包括任务发布大厅(topics),以及订阅任务(subscribe),发布任务(publish)
  let HunterUnion = {type: 'hunt',
        topics: Object.create(null),
       subscribe: function (topic, fn){
            if (!this.topics[topic]){
              this.topics[topic] = [];
            }
           this.topics[topic].push(fn);
            },
        publish: function (topic, money){
            if (!this.topics[topic])
              return;
            for (let fn of this.topics[topic]){
              fn(money)
            }
          }
      }

  //定义一个猎人类 包括姓名,级别
  function Hunter(name, level){
    this.name = name;
    this.level = level;
  }

  //猎人可在猎人工会发布订阅任务
  Hunter.prototype.subscribe = function (topic,fn){
      console.log(this.level + '猎人' + this.name + '订阅了狩猎' +topic + '的任务')
      HunterUnion.subscribe(topic, fn)
    }

  Hunter.prototype.publish = function (topic, money){
      console.log(this.level+ '猎人' + this.name + '发布了狩猎' + topic + '的任务')
      HunterUnion.publish(topic,money)
  }

  //猎人工会走来了几个猎人
   let hunterMing = new Hunter('小明', '黄金')
   let hunterJin = new Hunter('小金', '白银')
   let hunterZhang = new Hunter('小张', '黄金')
  let hunterPeter = new Hunter('Peter', '青铜')


  //小明,小金,小张分别订阅了狩猎tiger的任务
  hunterMing.subscribe('tiger', function (money){
        console.log('小明表示:' + (money > 200 ? '' : '不') + '接取任务')
      })
  hunterJin.subscribe('tiger', function (money){
    console.log('小金表示:接取任务')
  })
  hunterZhang.subscribe('tiger', function (money){
    console.log('小张表示:接取任务')
  })

  //Peter订阅了狩猎sheep的任务
  hunterPeter.subscribe('sheep', function (money){
        console.log('Peter表示:接取任务')
      })
  
  //Peter发布了狩猎tiger的任务
  hunterPeter.publish('tiger', 298)
  //猎人们发布(发布者)或订阅(观察者/订阅者)任务都是通过猎人工会(调度中心)关联起来的,他们没有直接的交流。

四、总结

观察者模式和发布订阅模式最大的区别就是发布订阅模式有个事件调度中心。

观察者模式由具体目标调度,每个被订阅的目标里面都需要有对观察者的处理,这种处理方式比较直接粗暴,但是会造成代码的冗余。

而发布订阅模式中统一由调度中心进行处理,订阅者和发布者互不干扰,消除了发布者和订阅者之间的依赖。这样一方面实现了解耦,还有就是可以实现更细粒度的一些控制。比如发布者发布了很多消息,但是不想所有的订阅者都接收到,就可以在调度中心做一些处理,类似于权限控制之类的。还可以做一些节流操作。