【deep JS】简单易懂的观察者模式和订阅者模式

274 阅读2分钟

设计模式太晦涩,我尽量用简洁的语言描述我对这两个模式的理解,觉得还是有不懂的或者存疑的地方,版聊讨论。

概念

观察者模式广泛应用于JS中。observer观察者是一个集合(collections),当状态有变化的时候,观察者通知集合下的对象相应的改变状态。举例说明这个概念。

概念图

代码实例

想象一下你有多个元素,他们需要同时更新内容当一些事件状态发生改变(例如:input中键入文字)。 他们既可以订阅观察者发送的通知,也可以取消观察者发送的通知。 可以在线玩一玩有需要可下载源代码

// define a class
class Observable {
  // each instance of the Observer class
  // starts with an empty array of things (observers)
  // that react to a state change
  constructor() {
    this.observers = [];
  }

  // add the ability to subscribe to a new object / DOM element
  // essentially, add something to the observers array
  subscribe(f) {
    this.observers.push(f);
  }

  // add the ability to unsubscribe from a particular object
  // essentially, remove something from the observers array
  unsubscribe(f) {
    this.observers = this.observers.filter(subscriber => subscriber !== f);
  }

  // update all subscribed objects / DOM elements
  // and pass some data to each of them
  notify(data) {
    this.observers.forEach(observer => observer(data));
  }
}

observer的用例:

// some DOM references
const input = document.querySelector('.js-input');
const p1 = document.querySelector('.js-p1');
const p2 = document.querySelector('.js-p2');
const p3 = document.querySelector('.js-p3');

// some actions to add to the observers array
const updateP1 = text => p1.textContent = text;
const updateP2 = text => p2.textContent = text;
const updateP3 = text => p3.textContent = text;

// instantiate new Observer class
const headingsObserver = new Observable();

// subscribe to some observers
headingsObserver.subscribe(updateP1);
headingsObserver.subscribe(updateP2);
headingsObserver.subscribe(updateP3);

// notify all observers about new data on event
input.addEventListener('keyup', e => {
  headingsObserver.notify(e.target.value);
});

结果页

与订阅者模式的对比

pub-sub模式多了一步publisher,它动态的接受着event

image.png

function pubSub() {
  const subscribers = {}

  function publish(eventName, data) {
    //没有订阅直接返回
    if (!Array.isArray(subscribers[eventName])) {
      return
    }
    //有订阅挨个执行函数
    subscribers[eventName].forEach((callback) => {
      callback(data)
    })
  }

  function subscribe(eventName, callback) {
    if (!Array.isArray(subscribers[eventName])) {
      subscribers[eventName] = []
    }
    subscribers[eventName].push(callback)
  }

  return {
    publish,
    subscribe,
  }
}

pub-sub最核心的代码就是这段,和observer的区别如下:

  1. 明显看出来和observer收集的数据结构就是不一样的,observer收集的是function,pubsub收集的是个object,{ eventName:callback }
  2. pubsub通过publish增加了一个动态的添加入口,observer是hardcore好的集合是死的。

总结:为什么要了解它

其实随着Vue和React这些响应式框架的流行,observer设计模式已经不太需要自己去写原生代码了,但原生代码会告诉你一些第一性原则,比如:

  1. observer是函数的collection
  2. publisher是对象的collection
  3. 如果是collection那么它也是iterator,都可以作为数据流来处理

那么UI events(click,mousemove等等)它们是observers,它们都可以当作数据流,瞬间有了一种天灵盖被钻孔的感觉。这可以更好的消除代码中的bug,提高质量和效率。


这部分有点深,英语能力好的同学可以先参考我找到的资料。

  1. “Learning JavaScript Design Patterns” by Addy Osmani
  2. pub-sub参考文献1
  3. pub-sub参考文献2
  4. pub-sub参考文献3
  5. pub-sub参考文献4