重学JS(九)—— 观察者模式和发布/订阅模式真不一样

有这么一段代码经常会出现在代码中

var pubsub = (()=>{
  var topics = {};
  function subscribe(topic,fn){
    if(!topics[topic]){
      topics[topic] = [];  
    }
    topics[topic].push(fn);
  }
  function publish(topic,...args){
    if(!topics[topic])
      return;
    for(let fn of topics[topic]){
      fn(...args);  
    }
  }
 return {
      subscribe,
      publish
  }
})()

测试代码

pubsub.subscribe('test',function(a,b){  //订阅者A订阅了test事件
  console.log(a,b);    
});
pubsub.publish('test','123','HH');   //123  HH(发布者B发布了test事件)

调用publish后打印出了123 HH。很奇妙的一段代码,当然实际上只是遍历了数组,然后把数组中的所有函数全部执行一遍而已。但是对于一个没读过实现代码的人来说,却是一个神奇的存在,JS居然能订阅发布消息,太酷了。
有的时候我会叫他观察者模式,有时候又会叫他发布订阅模式,觉得叫什么都是对的。
但是,他们并不一样。

差异

在观察者模式中,观察者需要直接订阅目标事件。在目标发出内容改变的事件后,直接接收事件并作出响应。发布订阅模式相比观察者模式多了个事件通道,订阅者和发布者不是直接关联的。
这段话可以看出上面的例子是发布订阅模式。订阅者A和发布者B是通过pubsub这个对象关联起来的,他们没有直接的交流。

那么真正的观察者模式是怎么样的?

一个或多个观察者对目标的状态感兴趣,通过将自己依附在目标对象上以便注册所感兴趣的内容。目标状态发生改变并且观察者可能对这些改变感兴趣,会发送一个通知消息,调用每个观察者的更新方法。当观察者不再对目标状态感兴趣时,他们可以简单将自己从中分离。

我们来实现下观察者模式。首先是目标的构造函数,他有个数组,用于添加观察者。还有个广播方法,遍历观察者数组后调用他们的update方法:

class Subject{
  constructor(){
    this.subs = [];
  }
  addSub(sub){
    this.subs.push(sub);
  }
  notify(){
    this.subs.forEach(sub=> {
      sub.update();
    });
  }
}

那么观察者就得有个update方法:

class Observer{
  update(){
    console.log('update');
  }
}

测试代码

let subject = new Subject();
let ob = new Observer();
//目标添加观察者了
subject.addSub(ob);
//目标发布消息调用观察者的更新方法了
subject.notify();   //update

可以看到目标和观察者是直接联系在一起的。观察者把自身添加到了目标对象中,可见和发布订阅模式差别还是很大的。在这种模式下,目标更像一个发布者,他让添加进来的所有观察者都执行了update函数,而观察者就像一个订阅者。

优劣

个人觉得发布/订阅模式比较简单,使用的也比较广泛。由于他订阅者和发布者不直接关联的特点我们完全可以把管理事件的对象写到一个单独文件中,作为库来使用。发布订阅模式中,双方不知道对方的存在,而观察者模式中,目标和观察者是直接联系起来的。具体选择什么模式,得视场景而定。一般来说,发布/订阅就够用了,简单清晰,符合我们dom事件编程的观念。
观察者模式哪里被用到过?vue的双向绑定。下篇就讲一下观察者模式在vue双向绑定实现中的运用。

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 1 场景问题# 1.1 订阅报纸的过程## 来考虑实际生活中订阅报纸的过程,这里简单总结了一下,订阅报纸的基本流程...
    七寸知架构阅读 4,725评论 5 57
  • 工厂模式类似于现实生活中的工厂可以产生大量相似的商品,去做同样的事情,实现同样的效果;这时候需要使用工厂模式。简单...
    舟渔行舟阅读 7,964评论 2 17
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 174,732评论 25 709
  • 静静的爱着这么一个人 从开始到时光的尽头 并不想遇见最好的你 而渴望相逢在故事的最初 那时风刚刚开始变暖 那时的我...
    卧龙饮水阅读 381评论 0 2
  • 推送技术产生场景: --服务器端主动性: 客户端与服务器交互都是客户端主动的, 服务器一般不能主动与客户端进行数据...
    原军锋阅读 34,928评论 4 60