Vue学习-设计模式探索

925 阅读3分钟

发布订阅模式

  • 订阅者
  • 发布者
  • 信号中心

我们假定,存在一个"信号中心",某个任务执行完成,就向信号中心"发布"(publish)一个信号,其他任务可以向信号中心"订阅"(subscribe)这个信号,从而知道什么时候自己可以开始执行。这就叫做"发布/订阅模式"(publish-subscribe pattern)

举一个例子,你在微博上关注了A,同时其他很多人也关注了A,那么当A发布动态的时候,微博就会为你们推送这条动态。A就是发布者,你是订阅者,微博就是信号中心,你和A是没有直接的消息往来的,全是通过微博来协调的(你的关注,A的发布动态)。

以Vue的自定义事件作为例子:

let vm = new Vue()

vm.$on('dataChange', () => {
    console.log('dataChange')
})

vm.$on('dataChange', () => {
    console.log('dataChange1')
})

vm.$emit('dataChange')

可能这样单独的拉出来看不太明白。那么我们来模拟自定义事件的实现

class EventEmitter {
    constructor () {
        // { eventType: [ handler1, handler2 ] }
        this.subs = {}
    }
    // 订阅通知
    $on (eventType, handler) {
        this.subs[eventType] = this.subs[eventType] || []
        this.subs[eventType].push(handler)
    }
    // 发布通知
    $emit (eventType) {
        if (this.subs[eventType]) {
            this.subs[eventType].forEach(handler => {
                handler()
            })
        }
    }
}

// 注册信息中心实例
var bus = new EventEmitter()

bus.$on('click',()=>{
    console.log('click')
})

bus.$on('click1',()=>{
    console.log('click1')
})

// 触发事件
bus.$emit('click')

EventEmitter 中,我们将所有的信息都存储在 subs 中,订阅者在 $on 将想要的事件类型和事件函数注册到信息中心里,当发布者通过 $emit 发布了该事件到信息中心,也就是该事件触发时,由信息中心统一处理订阅者注册到信息中心的代码。从而完成一次调度。

发布/订阅者模式 最大的特点就是实现了松耦合,也就是说你可以让发布者发布消息、订阅者接受消息,而不是寻找一种方式把两个分离的系统连接在一起。当然这种松耦合也是 发布/订阅者模式 最大的缺点,因为需要中间的代理,增加了系统的复杂度。而且发布者无法实时知道发布的消息是否被每个订阅者接收到了,增加了系统的不确定性。

观察者模式

观察者(Observer)直接订阅(Subscribe)主题(Subject),而当主题被激活的时候,会触发(Fire Event)观察者里的事件。

  • 观察者(订阅者) -- Watcher
    • update():当事件发生时,具体要做的事情
  • 目标(发布者) -- Dep
    • subs 数组:存储所有的观察者
    • addSub():添加观察者
    • notify():当事件发生,调用所有观察者的 update() 方法
  • 没有事件中心

如果你订阅了一份杂志或报纸,那就不需要再去报摊查询新出版的刊物了。出版社(即应用中的 “发布者”)会在刊物出版后(甚至提前)直接将最新一期寄送至你的邮箱中。

出版社负责维护订阅者列表,了解订阅者对哪些刊物感兴趣。当订阅者希望出版社停止寄送新一期的杂志时,他们可随时从该列表中退出。

以Vue中的 DepWatcher 来看:

// 发布者-目标
class Dep {
  constructor () {
    // 记录所有的订阅者
    this.subs = []
  }
  // 添加订阅者
  addSub (sub) {
    if (sub && sub.update) {
      this.subs.push(sub)
    }
  }
  // 发布通知
  notify () {
    this.subs.forEach(sub => {
      sub.update()
    })
  }
}
// 订阅者-观察者
class Watcher {
  update () {
    console.log('update')
  }
}

// 测试
let dep = new Dep()
let watcher = new Watcher()

dep.addSub(watcher)
dep.notify()

在这组代码中,Dep 指代的就是出版社,而 Watcher 就是你,你先去订阅了一份报纸,发布者将你记录起来。直到有新刊了,也就是事件触发了,发布者调用你的方法,将内容通知给你。

总结

观察者模式和发布订阅模式最大的区别就是发布订阅模式有个事件调度中心。

观察者模式由具体目标调度,每个被订阅的目标里面都需要有对观察者的处理,这种处理方式比较直接粗暴,但是会造成代码的冗余。

发布订阅模式 由统一由调度中心进行处理,订阅者和发布者互不干扰,消除了发布者和订阅者之间的依赖。这样一方面实现了解耦,还有就是可以实现更细粒度的一些控制。比如发布者发布了很多消息,但是不想所有的订阅者都接收到,就可以在调度中心做一些处理,类似于权限控制之类的。还可以做一些节流操作。