观察者模式

  • 发布 & 订阅
  • 1对N

示例

  • 点吃的,等待
  • 点喝的,等待

UML 类图

代码

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
// 目标-主题,保存状态,状态变化后触发(通知)所有观察者对象更新
class Subject {
constructor() {
this.state = 0;
this.observers = []
}
getState() {
return this.state;
}
setState(state) { // 目标发生变化时,调度观察者的更新方法
this.state = state;
this.notifyAllObservers();
}
notifyAllObservers() {
this.observers.forEach(observer => {
observer.update();
})
}
attach(observer) {
this.observers.push(observer);
}
}

// 观察者-提供更新接口
class Observer {
constructor(name, subject) {
this.name = name;
this.subject = subject;
this.subject.attach(this); // 观察者把自己注册到具体目标里
}
update() {
console.log(`${this.name} update, state: ${this.subject.getState()}`)
}
}

// 测试
const s = new Subject();
const o1 = new Observer('o1', s);
const o2 = new Observer('o2', s);
const o3 = new Observer('o3', s);

s.setState(1);
s.setState(2);
s.setState(3);

场景

1.网页事件绑定

2.Promise- 绑定 then,一旦状态发生变化,then 中函数就执行。比如打王者,一个(堆)人(观察者)猥琐地躲在草丛里,等待脆皮(目标)出现,小鲁班(目标)出现在附近(触发),全体(观察者)蜂拥上去一顿砍杀(更新)。

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
// 面向对象
function loadImg(src) {
let promise = new Promise(function(resolve, reject) {
let img = document.createElement('img');
img.onload = function(){
resolve(img);
}
img.onerror = function(){
reject('图片加载失败');
}
img.src = src;
});
return promise;
}

let imgUrl = 'http://ruizhengyun.cn/static/images/bg/bg_22.jpeg';

// 单一职责和
loadImg(imgUrl).then(function(img){
alert(`width: ${img.width}`);
return img;
}).then(function(img) {
alert(`height: ${img.height}`);
}).catch(function(ex){
alert(ex);
});

3.jQuery callbacks

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
<!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>观察者模式</title>
</head>
<body>
<script src="https://cdn.bootcss.com/jquery/3.3.0/jquery.js"></script>
<script>
// 自定义事件,自定义回调
var callbacks = $.Callbacks();

// 添加观察者
callbacks.add(function(info){
console.log('fn1', info);
});
callbacks.add(function(info){
console.log('fn2', info);
});
callbacks.add(function(info){
console.log('fn3', info);
});

// 触发
callbacks.fire('Are you ready?');
callbacks.fire('gogogo');
</script>
</body>
</html>

4.nodejs 自定义事件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 观察者- nodejs 自定义事件
const EventEmitter = require('events').EventEmitter;

class Dog extends EventEmitter {
constructor(name){
super();
this.name = name;
}
}

// 测试
const simon = new Dog('simon');
simon.on('dark', function() {
console.log(`${this.name} barked`)
})

setTimeout(function() {
simon.emit('dark');
}, 1000)

5.nodejs 自定义事件-读取文件 observer-fs.js

1
2
3
4
5
6
7
8
9
10
11
12
// 观察者- fs
const fs = require('fs');
const readStream = fs.createReadStream('./data/file.txt');

var length = 0;
readStream.on('data', function(chunk) {
length += chunk.toString().length
})

readStream.on('end', function() {
console.log('length', length)
})

1
2
// 命令行
node observer-fs.js

6.nodejs 自定义事件-逐行读取文件 observer-readline.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 观察者- readline
var readline = require('readline');
var fs = require('fs');

var rl = readline.createInterface({
input: fs.createReadStream('./data/file.txt')
})

var lineNum = 0;
rl.on('line', function(line) {
lineNum++;
console.log(`lineNum ${lineNum < 10 ? '0' + lineNum : lineNum} content is: ${line}`)
})
rl.on('close', function() {
console.log(`lineNum total: `, lineNum);
})

1
2
3
4
5
// 安装readline
npm install -S readline

// 命令行
node observer-readline.js

7.nodejs 中,处理 http 请求;多进程通讯


8.