订阅发布模式和观察者模式的区别

首先咱们须要了解二者的定义和实现的方式,才能更好的区分二者的不一样点。javascript

或许之前认为订阅发布模式是观察者模式的一种别称,可是发展至今,概念已经有了很多区别。html

订阅发布模式

软件架构中,发布-订阅是一种消息范式,消息的发送者(称为发布者)不会将消息直接发送给特定的接收者(称为订阅者)。而是将发布的消息分为不一样的类别,无需了解哪些订阅者(若是有的话)可能存在。一样的,订阅者能够表达对一个或多个类别的兴趣,只接收感兴趣的消息,无需了解哪些发布者(若是有的话)存在。前端

或许你用过 eventemitter、node 的 events、Backbone 的 events 等等,这些都是前端早期,比较流行的数据流通讯方式,即订阅发布模式java

从字面意思来看,咱们须要首先订阅,发布者发布消息后才会收到发布的消息。不过咱们还须要一个中间者来协调,从事件角度来讲,这个中间者就是事件中心,协调发布者和订阅者直接的消息通讯。node

完成订阅发布整个流程须要三个角色:数组

  • 发布者
  • 事件中心
  • 订阅者

以事件为例,简单流程以下:架构

发布者->事件中心<=>订阅者,订阅者须要向事件中心订阅指定的事件 -> 发布者向事件中心发布指定事件内容 -> 事件中心通知订阅者 -> 订阅者收到消息(多是多个订阅者),到此完成了一次订阅发布的流程。app

简单的代码实现以下:dom

class Event {
  constructor() {
    // 全部 eventType 监听器回调函数(数组)
    this.listeners = {}
  }
  /** * 订阅事件 * @param {String} eventType 事件类型 * @param {Function} listener 订阅后发布动做触发的回调函数,参数为发布的数据 */
  on(eventType, listener) {
    if (!this.listeners[eventType]) {
      this.listeners[eventType] = []
    }
    this.listeners[eventType].push(listener)
  }
  /** * 发布事件 * @param {String} eventType 事件类型 * @param {Any} data 发布的内容 */
  emit(eventType, data) {
    const callbacks = this.listeners[eventType]
    if (callbacks) {
      callbacks.forEach((c) => {
        c(data)
      })
    }
  }
}

const event = new Event()
event.on('open', (data) => {
  console.log(data)
})
event.emit('open', { open: true })
复制代码

Event 能够理解为事件中心,提供了订阅和发布功能。函数

订阅者在订阅事件的时候,只关注事件自己,而不关心谁会发布这个事件;发布者在发布事件的时候,只关注事件自己,而不关心谁订阅了这个事件。

观察者模式

观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个目标对象,当这个目标对象的状态发生变化时,会通知全部观察者对象,使它们可以自动更新。

观察者模式咱们可能比较熟悉的场景就是响应式数据,如 Vue 的响应式、Mbox 的响应式。

观察者模式有完成整个流程须要两个角色:

  • 目标
  • 观察者

简单流程以下:

目标<=>观察者,观察者观察目标(监听目标)-> 目标发生变化-> 目标主动通知观察者。

简单的代码实现以下:

/** * 观察监听一个对象成员的变化 * @param {Object} obj 观察的对象 * @param {String} targetVariable 观察的对象成员 * @param {Function} callback 目标变化触发的回调 */
function observer(obj, targetVariable, callback) {
  if (!obj.data) {
    obj.data = {}
  }
  Object.defineProperty(obj, targetVariable, {
    get() {
      return this.data[targetVariable]
    },
    set(val) {
      this.data[targetVariable] = val
      // 目标主动通知观察者
      callback && callback(val)
    },
  })
  if (obj.data[targetVariable]) {
    callback && callback(obj.data[targetVariable])
  }
}
复制代码

可运行例子以下:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,viewport-fit=cover" />
    <title></title>
  </head>
  <body>
    <div id="app">
      <div id="dom-one"></div>
      <br />
      <div id="dom-two"></div>
      <br />
      <button id="btn">改变</button>
    </div>
    <script> /** * 观察监听一个对象成员的变化 * @param {Object} obj 观察的对象 * @param {String} targetVariable 观察的对象成员 * @param {Function} callback 目标变化触发的回调 */ function observer(obj, targetVariable, callback) { if (!obj.data) { obj.data = {} } Object.defineProperty(obj, targetVariable, { get() { return this.data[targetVariable] }, set(val) { this.data[targetVariable] = val // 目标主动通知观察者 callback && callback(val) }, }) if (obj.data[targetVariable]) { callback && callback(obj.data[targetVariable]) } } const obj = { data: { description: '原始值' }, } observer(obj, 'description', value => { document.querySelector('#dom-one').innerHTML = value document.querySelector('#dom-two').innerHTML = value }) btn.onclick = () => { obj.description = '改变了' } </script>
  </body>
</html>
复制代码

二者的区别在哪?

角色角度来看,订阅发布模式须要三种角色,发布者、事件中心和订阅者。二观察者模式须要两种角色,目标和观察者,无事件中心负责通讯。

从耦合度上来看,订阅发布模式是一个事件中心调度模式,订阅者和发布者是没有直接关联的,经过事件中心进行关联,二者是解耦的。而观察者模式中目标和观察者是直接关联的,耦合在一块儿(有些观念说观察者是解耦,解耦的是业务代码,不是目标和观察者自己)。

二者的优缺点?

优缺点都是从前端角度来看的。

订阅发布模式优势

  • 灵活

    因为订阅发布模式的发布者和订阅者是解耦的,只要引入订阅发布模式的事件中心,不管在何处均可以发布订阅。同时订阅发布者相互之间不影响。

订阅发布模式在使用不当的状况下,容易形成数据流混乱,因此才有了 React 提出的单项数据流思想,就是为了解决数据流混乱的问题。

订阅发布模式缺点

  • 容易致使代码很差维护

    灵活是有点,同时也是缺点,使用不当就会形成数据流混乱,致使代码很差维护。

  • 性能消耗更大

    订阅发布模式须要维护事件列队,订阅的事件越多,内存消耗越大。

观察者模式优势

  • 响应式

    目标变化就会通知观察者,这是观察者最大的有点,也是由于这个优势,观察者模式在前端才会这么出名。

观察者模式缺点

  • 不灵活

    相比订阅发布模式,因为目标和观察者是耦合在一块儿的,因此观察者模式须要同时引入目标和观察者才能达到响应式的效果。而订阅发布模式只须要引入事件中心,订阅者和发布者能够再也不一处。

参考文章

订阅发布模式和观察者模式真的不同

相关文章
相关标签/搜索