什么是观察者模式

一个或多个观察者(Observer)对目标(Subject)的状态感兴趣,它们通过将自己依附(Add(Observer))在目标(Subject)对象上以便注册所感兴趣的内容。目标(Subject)状态发生改变并且观察者可能对这些改变感兴趣时,就会发送(Notify)一个通知消息,调用每个观察者(Observer)的更新方法(update)。当观察者不再对目标感兴趣时,它们可以简单的将自己从中分离(Remove(Observer))。

从上面的定义中,我们可以抽象出几个概念

  • 目标(Subject)
  • 观察者(Observer)
  • 注册事件(Subject.Add(Observer))
  • 取消注册事件(Subject.Remove(Observer))
  • 发送通知事件(Subject.Notify)
  • 更新观察者事件(Observer.update)

我们可以用以下的类图来描述观察者模式

观察者模式 - 图1

观察者模式的简单实现

Subject

  1. function Subject () {
  2. // 用于存储全部的observer
  3. this.observers = []
  4. }
  5. Subject.prototype.Add = function (observer) {
  6. this.observers.push(observer)
  7. }
  8. Subject.prototype.Remove = function (observer) {
  9. this.observers.splice(this.observers.indexOf(observer), 1)
  10. }
  11. Subject.prototype.Notify = function (context) {
  12. this.observers.forEach(function (observer) {
  13. observer.update(context)
  14. })
  15. }

Observer

  1. function Observer () {
  2. this.update = function () {
  3. // observer需要具体实现自己的update方法
  4. }
  5. }

由于每一个Observer都是不一样的,所以这里我们提供一个extendObserver方法,目的是把一些普通对象改造成Observer对象(也可以用类继承的方式实现,这里只是简化处理)

  1. function extendObserver (obj) {
  2. var observerInstance = new Observer()
  3. for (var key in obj) {
  4. // 这样可以保证传入的obj中的update方法可以覆盖Observer中的update方法
  5. observerInstance[key] = obj[key]
  6. }
  7. return observerInstance
  8. }

业务场景模拟

假设我们有这样一个场景,有一个输入框a,另外有两个div b和c。b和c想要监听到输入框A的onchange事件,并执行自己的处理,b显示a的值,c显示’hello:’+a。

  1. var elA = document.getElementById('a')
  2. var elB = document.getElementById('b')
  3. var elC = document.getElementById('c')
  4. // 实例化 subject和observer对象
  5. var subjectA = new Subject()
  6. elB.update = function (context) {
  7. elB.innerHTML = context
  8. }
  9. elC.update = function (context) {
  10. elC.innerHTML = 'hello:' + context
  11. }
  12. var observerB = extendObserver(elB)
  13. var observerC = extendObserver(elC)
  14. // 注册监听
  15. subjectA.Add(observerB)
  16. subjectA.Add(observerC)
  17. elA.onchange = function () {
  18. // 如果不通观察者实现,则需要把update方法里的所有内容在这里写一遍,但实际A并不关心B和C都需要做出哪些响应
  19. subjectA.Notify(elA.value)
  20. }

结合上面的例子,观察者模式的优点就在与,在onchange事件中,我不必去关注观察者监听后的具体实现,将具体的实现进行解藕,交给观察者自己维护

具体里子可以参见https://github.com/yonyou-xtech/book-code/blob/master/设计模式/Observer.html

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

大多数人会将观察者模式和发布订阅模式混淆在一起,但实际上这两者很相似但还是有一定的差异的。
发布/订阅模式使用了一个事件通道/调度中心来避免订阅者和发布者产生依赖关系。观察者则由具体的目标(Subject)调用观察者的update方法进行调度。发布/订阅模式更常用于全局的事件总线,如nodejs中的eventEmit。

观察者模式的实际场景的应用

  • Vuejs的双向数据绑定(具体可以参考【前端框架】->【Vuejs】vue响应式原理章节)
  • Rxjs
  • Mobx