# Observer 观察者模式

一个对象(称为 subject)维持一系列依赖于它(观察者)的对象,将有关状态的任何变更自动通知给它们

  • Subject: 目标,维护一系列的观察者,方便添加或删除观察者
  • Observer: 观察者,为那些在目标状态发生改变时或得通知的对象提供一个更新接口
  • ConcreteSubject: 具体目标,状态发生改变时,向 Observer 发出通知,储存 ConcreteObserver 的状态
  • ConcreteObserver: 具体观察者,存储一个指向 ConcreteSubject 的引用,实现 Observer 的更新接口,以实现自身状态与目标的状态保持一致

# Observer 观察者模式实现

// 观察者列表
function ObserverList () {
  this.observerList = []
}
ObserverList.prototype.Add = function (obj) {
  this.observerList.push(obj)
}
ObserverList.prototype.Empty = function () {
  this.observerList = []
}
ObserverList.prototype.Count = function () {
  return this.observerList.length
}
ObserverList.prototype.Get = function (index) {
  if (index > -1 && index < this.observerList.length) {
    return this.observerList[index]
  }
  return null
}
ObserverList.prototype.Insert = function (obj, index) {
  let pointer = -1
  pointer = Max.max(index, 0)
  pointer = Math.min(index, this.observerList.length)

  this.observerList = [...this.observerList.slice(0, index), obj, ...this.observerList.slice(index)]

  return pointer
}
ObserverList.prototype.IndexOf = function (obj, startIndex) {
  var i = startIndex
  var pointer = -1

  while (i < this.observerList.length) {
    if (this.observerList[i] === obj) {
      pointer = i
      break
    }
    i++
  }
  return pointer
}
ObserverList.prototype.RemoveIndexAt = function (index) {
  let pointer = -1
  pointer = Max.max(index, 0)
  pointer = Math.min(index, this.observerList.length)

  this.observerList.splice(pointer, 1)
}

function extend (obj, extension) {
  for (var key in obj) {
    extension[key] = obj[key]
  }
}

// Subject 维护一系列的观察者
function Subject () {
  this.observers = new ObserverList()
}

Subject.prototype.AddObserver = function (observer) {
  this.observers.Add(observer)
}
Subject.prototype.RemoveObserve = function (observer) {
  this.observers.RemoveIndexAt(this.observers.IndexOf(observer, 0))
}
Subject.prototype.Notify = function (context) {
  var observerCount = this.observers.Count()
  for (let i = 0; i < observerCount; i++) {
    this.observers.Get(i).Update(context)
  }
}
// Observer
function Observer () {
  this.update = function () {
    console.log('get notify')
    throw new Error('the update method thould be defined by the observer')
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79

使用上述功能:

<button id="addNewObserver">Add new Observer checkbox</button>
<input type="checkbox" id="mainCheckbox">
<div id="observersContainer"></div>
1
2
3
var controlCheckbox = document.getElementById('mainCheckbox')
var addBtn = document.getElementById('addNewObserver')
var container = document.getElementById('observersContainer')

// 具体目标 Concrete Subject

// 利用 Subject 拓展 controlCheckbox
extend(new Subject(), controlCheckbox)

// 点击 checkbox 会触发通知到观察者上
controlCheckbox['onclick'] = function () {
  controlCheckbox.Notify(controlCheckbox.checked)
}

addBtn['onclick'] = AddNewObserver

function AddNewObserver() {
  // 创建需要添加的新 checkbox
  var check = document.createElement('input')
  check.type = 'checkbox'

  extend(new Observer(), check)
  // 重写更新行为
  check.Update = function (value) {
    this.checked = value
  }

  controlCheckbox.AddObserver(check)
  container.appendChild(check)
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

# Observer 观察者模式和 Publish/Subscribe 发布/订阅 模式的区别

Observer 模式要求希望接收到主题通知的观察者(或者对象)必须订阅内容改变的事件

Publish/Subscribe 模式使用了一个 主题/事件 通道,这个通道介于希望接收到通知(订阅者)的对象和激活事件的对象(发布者)之间。该事件系统允许代码定义应用程序的特定事件,这些事件可以传递自定义参数,自定义参数包含订阅者所需要的值。其目的在于避免订阅者与发布者之间产生依赖关系。

observer-vs-publish-subscribe

优点:Observer 模式和 Publish/Subscribe 模式鼓励我们努力思考应用程序不同部分之间的关系。无需使类紧密耦合

缺点:如果订阅者执行日志奔溃了,发布者无法看到。订阅者非常无视彼此的存在,并对变换发布者产生的成本视而不见。由于订阅者和发布者之间的动态关系,很难跟踪依赖更新。

# Publish/Subscribe 实现