目录

观察者模式与发布订阅

观察者模式

一个典型的观察者模式应用场景是用户在一个网站订阅主题

多个用户(观察者,Observer)都可以订阅某个主题(Subject),当主题内容更新时订阅该主题的用户都能收到通知

Subject是构造函数,new Subject() 创建一个主题对象,该对象内部维护订阅当前主题的观察者数组。主题对象上有一些方法,如添加观察者(addObserver)、删除观察者(removeObserver)、通知观察者更新(notify)。 当notify 时实际上调用全部观察者 observer 自身的 update 方法。

Observer 是构造函数,new Observer() 创建一个观察者对象,该对象有一个 update 方法。

class Subject {
  constructor() {
    this.observers = []
  }
  addObserver(observer) {
    this.observers.push(observer)
  }
  removeObserver(observer) {
    this.observers = this.observers.filter(o !== observer)
  }
  notify() {
    this.observers.forEach(observer => observer.update())
  }
}
 
class Observer{
  constructor() {
    this.update = function() {}
  }
}
let subject = new Subject()
let observer1 = new Observer()
observer1.update = function() {
  console.log('observer1 update')
}
subject.addObserver(observer1)
 
 
let observer2 = new Observer('valley')
observer2.update = function() {
  console.log('observer2 update')
}
subject.addObserver(observer2)
 
subject.notify()

上面的代码中,主题被观察者订阅的写法是 subject.addObserver(observer), 不是很直观,给观察者增加订阅方法

class Observer{
  constructor() {
    this.update = function() {}
  }
  subscribeTo(subject) {
    subject.addObserver(this)
  }
}
let subject = new Subject()
let observer = new Observer()
observer.update = function() {
  console.log('observer update')
}
observer.subscribeTo(subject)  //观察者订阅主题
subject.notify()

发布订阅/事件管理器

const EventManager = (function(){
  let eventList = {}
  function on(event, handler) {
    if(!eventList[event]) {
      eventList[event] = [handler]
    }else {
      eventList[event].push(handler)
    }
  }
  function fire(event, data) {
    if(eventList[event]) {
      eventList[event].forEach(handler => handler(data))
    }
  }
  function off(event, handler) {
    if(eventList[event]) {
      if(!handler) {
        delete eventList[event]
      }else {
        let index = eventList[event].indexOf(handler)
        eventList[event].splice(index, 1)
      }
    }
  }
  return {
    on: on,
    fire: fire,
    off: off
  }
})()
 
EventManager.on('sayHello', function(data) {
  console.log('hello ' + data)
})
 
EventManager.fire('sayHello', 'jirengu')