# 观察者模式:Observer
这个是一个学习笔记,嗯,相信很多同学和我一样,并没有真正搞明白观察者模式和发布订阅模式到底有什么区别。于是在网上各种搜索资料,但其实解释并不是很清楚,各种看书找资料,坚持把各种观察者模式的用例都放在这里,方便回顾和学习。
站在巨人的肩膀上学习观察者模式。观察者模式的各种使用场景会在持续更新中。
## 概念
- 主题: Subject // 维护一些列的观察者,方便添加或者删除观察者。
- 观察者: Observer // 提供更新接口
- 具体的目标: ConcreteSubject // 状态发生改变的时候,向观察者发发出通知
- 具体的观察者: ConcreteObserver // 存储一个 ConcreteSubject 的引用。
## 图理解
## 文字理解
观察者模式: Observer 是通过一个对象(或者主题 Subject, 或者成为目标)来来维持一些列观察者的对象。当主题 Subject 有变化的时候,会通知观察者。
一个主题(或者目标)是通过类似于广播的形式来告诉观察者有关的通知。当不希望特定的观察者继续获取通知的时候,我们可将通过Subject将其从观察者列表中移除。
也就是说观察者模式包含了一个观察者列表,所有的观察者全部放在这个这个列表中,当某个列表不在需要观察服务后,可以从列表中移除。
一个或者多个观察者对目标感兴趣,他们通过将自己依附在目标对象上以便注册所感兴趣的内容。目标状态发生了改变并且观察者可能对这些感兴趣,就会发送一个通知消息,调用每个观察者的更新方法。当观察者不在对目标感兴趣时,他们可以简单的将自己从中分离。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>Observer</title>
</head>
<body>
<button id="addNewObserver">Add New Observer checkbox</button>
<input type="checkbox" id="mainCheckbox" />
<div id="observersContainer"></div>
</body>
<script src="./script.js"></script>
</html>
function ObserverList() {
this.observerList = []
}
ObserverList.prototype.Add = function (obj) {
return 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]
}
}
ObserverList.prototype.Insert = function (obj, index) {
var point = -1;
if (index === 0) {
this.observerList.unshift(obj)
pointer = index
} else if (index === this.observerList.length) {
this.observerList.push(obj)
pointer = index
}
return pointer
}
ObserverList.prototype.IndexOf = function (obj, startIndex) {
var i = startIndex, pointer = -1;
while (i < this.observerList.length) {
if (this.observerList.length[i] === obj) {
pointer = i
}
i++
}
return pointer
}
ObserverList.prototype.RemoveIndexAt = function (index) {
if (index === 0) {
this.observerList.shift()
} else if (index === this.observerList.length - 1) {
this.observerList.pop()
}
}
function extend(obj, extension) {
for (var key in obj) {
extension[key] = obj[key]
}
}
function Subject() {
this.observers = new ObserverList();
}
Subject.prototype.AddObserver = function (observer) {
this.observers.Add(observer)
}
Subject.prototype.removeObserver = function (observer) {
this.observers.RemoveIndexAt(this.observers.IndexOf(observer, 0))
}
Subject.prototype.Notify = function (context) {
var observerCount = this.observers.Count()
for (var i = 0; i < observerCount; i++) {
this.observers.Get(i).Update(context)
}
}
function Observer() {
// 需要被重写
this.Update = function () {
// some code
}
}
var controlCheckbox = document.getElementById('mainCheckbox'),
addBtn = document.getElementById('addNewObserver'),
container = document.getElementById('observersContainer'),
notify = document.getElementById('notify')
// controlCheckbox 上扩展主题的方法 AddObserver,removeObserver, Notify
extend(new Subject(), controlCheckbox)
controlCheckbox["onclick"] = new Function("controlCheckbox.Notify(controlCheckbox.checked)")
addBtn["onclick"] = AddNewObserver;
function AddNewObserver() {
var check = document.createElement("input")
check.type = "checkbox"
// check 上添加 Update 属性
extend(new Observer(), check)
check.Update = function (value) {
debugger
this.checked = value;
}
controlCheckbox.AddObserver(check)
container.appendChild(check)
}
在这个例子中,主题 Subject 通过 notify 方法通知所有的观察者更新数据。
- 主题 Subject
- 具体主题是 controlCheckbox
- 观察这是 Observer,通过 extends 给 check 赋能 Update 方法。
- 具体的观察者: 每一个 check
## 观察者模式与发布订阅模式的区别
观察者模式是通过主题直接就通知了观察者。中间没有多余的层级关系。但是发布订阅模式不同,发布订阅通知不是直接关系,存在一个中间层。发布订阅会在后面的继续文章深入探索。因为发布订阅和观察模式,在前端太常用了!
## 参考
- [JavaScript 设计模式](Addy Osmani著作,徐涛 翻译)