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

100 阅读2分钟

观察者模式(Observer Pattern)

当对象间存在一对多关系时,一个对象(observed)被修改时,则会自动通知依赖它的对象(observer)

观察者模式主要有两个主体:被观察者/目标对象(observed)、观察者(observer)

  • 被观察者/目标对象(observed)

    • 观察者列表(observerList
    • 通知方法notity(自身发生变化时调用这个方法通知观察者们)
    • 添加观察者到列表(addObserver
  • 观察者(observer)实现update方法供目标对象调用,update方法中实现自定义逻辑

// 目标对象
class Observed {
  constructor() {
    // 初始化观察者列表
    this.observerList = []
  }

  // 添加观察者到列表
  addObserver(observer) {
    this.observerList.push(observer)
  }

  // 执行操作并通知依赖的观察者
  notity(msg) {
    this.observerList.forEach((n) => n.update(msg))
  }
}

// 观察者
class Observer {
  constructor(name) {
    this.name = name
  }

  // 实现update方法
  update(msg) {
    console.log(`${this.name},${msg}`)
  }
}

let observed = new Observed()
let observer1 = new Observer('阿坤')
let observer2 = new Observer('你干嘛~')

// 添加依赖
observed.addObserver(observer1)
observed.addObserver(observer2)

// 执行操作
observed.notity('唱,跳,rap,打篮球')
// 输出结果
阿坤,唱,跳,rap,打篮球
你干嘛~,唱,跳,rap,打篮球

发布订阅模式

订阅者(Subscriber)把自己想订阅的事件注册(Subscribe)到调度中心(Event Channel),当发布者(Publisher)发布该事件(Publish Event)到调度中心,也就是该事件触发时,由调度中心统一调度(Fire Event)订阅者注册到调度中心的处理代码

class EventChannel {
  constructor() {
    /*
     事件中心其实就是一个对象,存放键值对
     键就是订阅的事件
     值就是一个数组包含多个事件函数
    */
    this.events = {}
  }

  // 订阅也就是注册事件
  subscribe(eventName, callback) {
    if (!this.events[eventName]) {
      this.events[eventName] = this.events[eventName] || []
    }

    this.events[eventName].push(callback)
  }

  // 发布方法
  publish(eventName, data) {
    if (this.events[eventName]) {
      this.events[eventName].forEach((cb) => cb(data))
    }
  }

  // 取消订阅
  unsubscribe(eventName) {
    if (this.events[eventName]) {
      let newEvent = {}
      for (let key in this.events) {
        if (key !== eventName) {
          newEvent[key] = this.events[key]
        }
      }
      this.events = newEvent
    }
  }
}

// 创建调度中心
let events = new EventChannel()

/* 订阅者订阅事件比传入事件函数 */
// 订阅事件一
events.subscribe('rap', (data) => {
  console.log(`${data.name}会rap`)
})

events.subscribe('rap', (data) => {
  console.log(`${data.name}会rap,叽叽叽叽`)
})

// 订阅事件二
events.subscribe('singAndDance', (data) => {
  console.log(`${data.name}会唱跳`)
})

// 订阅事件三
events.subscribe('playBasketball', (data) => {
  console.log(`${data.name}会打篮球`)
})

/* 发布者发布事件 */
events.publish('rap', { name: '糖果超甜' })
events.publish('playBasketball', { name: '小坤脚' })
events.publish('playBasketball', { name: '啊坤' })
// 输出结果
糖果超甜会rap
糖果超甜会rap,叽叽叽叽
小坤脚会打篮球
啊坤会打篮球

总结

观察者模式是直接观察目标对象,目标对象发送通知,无论观察者是否想收到通知都会收到,而发布订阅中间多了一个调度中心,只有订阅了该事件才会收到通知

Vue 中,数据双向绑定使用的是观察者模式,事件总线EventBus使用的是发布订阅模式

举个找房例子,观察者模式就是我们直接面向房东,而发布订阅就类似上平台或者找中介,只有发布了我们才能看到