发布-订阅模式,简单了解

662 阅读2分钟

发布-订阅模式,简单了解。

  • 观察者模式(Observer)
  • 发布订阅模式(Pubilsh/Subscribe)
  • 总结

1.观察者模式(Observer)

观察者模式,从字面理解至少需要一个观察者,如果有观察者,猜想也还需要被观察的主题?

文字描述如下:

观察者模式是一种设计模式:其中一个对象(称为Subject),维持一系列依赖于它的(观察者)对象,将有关状态的任何变更自动通知给它们(观察者)

画图描述如下:

Subject(目标):维护一系列的(对这个主题感兴趣)观察者,方便 添加,删除,通知观察者

Observer(观察者):在(感兴趣的)主题通知观察者自己状态变化时,做改变。观察者提供一个更新接口(更新自己)

有了以上的概念,代码简单实现如下:

// 主题,接收对自己感兴趣的观察者,触发每个观察者
class Subject {
  constructor(name) {
    this.state = 0
    this.name = name
    // 存储一系列的(对这个主题感兴趣)观察者
    this.observers = []
  }
  addObservers(observer) {
    // 添加
    this.observers.push(observer)
  }
  removeObservers(observer) {
    // 删除
    this.observers = tthis.observers.filter(item => item != observer)
  }
  notifyObservers() {
    // 通知
    var observers = this.observers
    for (var i = 0, j = observers.length; i < j; i++) {
      observers[i].update(this.name)
    }
  }
  changeState(state) {
    this.state = state
  }
}
// 观察者,等待被触发
class Observer {
  constructor(name) {
    this.name = name
  }
  update(topic) {
    // 更新自己,对主题变化做响应
    console.log(`${this.name}订阅的${topic}更新了, ${this.name}: 做了更新操作`)
  }
  subscribe(subject) {
    subject && subject.addObservers(this)
  }
}
let book = new Subject('book')
let  coffee = new Subject('coffee')
let xiaobu = new Observer('小布')

// xiaobu 对book这个主题感兴趣,订阅了book
xiaobu.subscribe(book)
// xiaobu 对coffee感兴趣,订阅了coffee
xiaobu.subscribe(coffee)

book.changeState(2)
book.notifyObservers()

在实际项目框架中的应用,举个例子(不了解vue的,可以暂时跳过,不影响理解):

Vue的数据双向绑定实现就采用了观察者模式。

主题 Dep ,观察者Watcher 。

数据在get部分:收集依赖

数据在set部分:派发通知

代码部分截取:

export default class Dep {
  constructor () {
    this.id = uid++
    this.subs = []
  }
  addSub (sub: Watcher) { // 添加
    this.subs.push(sub)
  }
  removeSub (sub: Watcher) {  // 删除
    remove(this.subs, sub)
  }
  depend () { // 调用观察者的 添加感兴趣的主题
    if (Dep.target) {// (Dep.target 记录了当前的观察者
      Dep.target.addDep(this)  // 观察者 对这个 主题感兴趣
    }
  }
  notify () { // 通知
    for (let i = 0, l = subs.length; i < l; i++) {
      subs[i].update()
    }
  }
}
class Watcher {
  constructor (
    vm: Component,
    cb: Function
  ) {
    this.vm = vm
    this.cb = cb
  }
  addDep (Dep) { // 添加感兴趣的主题
    dep.addSub(this)
  }
  update () { // 触发更新
    /* istanbul ignore else */
    if (this.lazy) {
      this.dirty = true
    } else if (this.sync) {
      this.run()
    } else {
      queueWatcher(this)
    }
  }
}

// 数据响应式
function defineReactive (object, key, value) {
  // dep 存储一系列的(对这个数据感兴趣)观察者
  const dep = new Dep()
  Object.defineProperty(obj, key, {
    get() {
      if (Dep.target) { // 观察者添加主题
        dep.depend() //  dep.depend() = Dep.target.addDep(this) 
      }
    },
    set(newVal) {
      dep.notify() // 通知所有人数据更新了
    }
  })
}

2.发布/订阅模式(Pubilsh/Subscribe)

在JavaScript 讲设计模式的书里面 通常会看见一句话, 发布订阅模式 又叫做 观察者模式

发布订阅模式 是 观察者模式 的一种实现(有所变化)

文字描述如下:

发布订阅(Publish/Subscriber)模式使用了一个 主题/事件通道对象,管理 主题事件

Publish:发布者,发布通知 (通知 主题/事件通道对象 这个主题变化了,执行对应的事件)

Subscriber:订阅者,注册主题,和事件( 告诉 主题/事件通道对象,这个主题变换,对应执行事件)

画图描述如下:

有了以上的概念,代码简单实现如下:

let eventTopic = {}
class Pushlish { // 发布者,发布通知 (通知 主题/事件通道对象  这个主题变化了,执行对应的事件)
  update (topic, ...rest) {
    eventTopic[topic]&&eventTopic[topic].forEach(listener => listener.apply(null,rest))
  }
}
class Subscriber { //  订阅者,注册主题,和事件( 告诉 主题/事件通道对象,这个主题变换,对应执行事件)
  subscribe (topic,event) {
    if(eventTopic[topic]) {
      eventTopic[topic].push(event)
    } else {
      eventTopic[topic] = [event]
    }
  }
}
let publish = new Pushlish()
let subscribe = new Subscriber()
subscribe.subscribe('book',(name)=>{
  console.log(`${name}订阅的book更新了, ${name}: 做了更新操作`)
})
publish.update('book','小布')

然后代码分装整合如下:

class EventTopic {
  constructor() {
    this._eventTopic = {}
  }
  subscribe (topic,event) {
    if(this._eventTopic[topic]) {
      this._eventTopic[topic].push(event)
    } else {
      this._eventTopic[topic] = [event]
    }
  }
  update (topic, ...rest) {
    this.events[type]&&this.events[type].forEach(listener => listener.apply(this,rest))
  }
}
let eventTopic = new EventTopic()
eventTopic.subscribe('book',(name)=>{
  console.log(`${name}订阅的book更新了, ${name}: 做了更新操作`)
})
eventTopic.update('book','小布')

在实际项目框架中的应用,举个例子(不了解node的,可以暂时跳过,不影响理解)

node的events 模块:github.com/nodejs/node…

3.总结

使用Observer模式,维护相关对象之间的关系,另一种Observer模式Pubilsh/Subscribe模式是解耦了对象。

文中例子比较粗糙,理解不准确之处,还请教正。