设计模式之发布-订阅者模式

asynchronous

简介

发布-订阅者模式也叫观察者模式。定义了一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖它的对象都会收到通知。

什么是发布-订阅者模式

以现实生活中一个事件为例,比如你要结婚了,作为消息发布者,打开通讯录,挨个打电话通知各个朋友结婚的消息。将这个过程抽象为:

  1. 发布者(你)
  2. 事件缓存列表(通讯录,朋友订阅了你的所有消息)
  3. 发布消息时候遍历事件缓存列表,依次触发订阅者的回调函数(挨个打电话)
  4. 此外,回调函数种可以添加其他的参数,比如告诉他们时间、地点。订阅者收到这些消息时可以进行不同的处理。

showCode

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
// 发布者
let Mymsg = {}

// 订阅者事件缓存列表(通讯录)
Mymsg.peopleList = []

// 订阅者回调函数(通讯录列表中的电话)
Mymsg.listen = function(fn) {
this.peopleList.push(fn)
}

Mymsg.listen( name => {
console.log(`${name} 收到你结婚的消息了!`)
})

Mymsg.listen( () => {
console.log('Hia~Hia')
})

// 执行订阅者回调函数(挨个打电话)
Mymsg.triger = function() {
let list = this.peopleList
for(let i = 0,fn;fn=list[i++];i=list.length) {
fn.apply(this,arguments)
}
}

Mymsg.triger('Zhangsan')
Mymsg.triger('Lisi')
  • 这就是发布-订阅者的简单实现。订阅者会受到每一个发布的消息,如果他想收到他希望听到的消息,比如:李四的心理阴暗,他就想知道你失业了,就需要添加一个key,让订阅者订阅自己感兴趣的消息。
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
let Mymsg = {}
Mymsg.peopleList = []
Mymsg.listen = function(key,fn) {

// 如果没有订阅此类消息,创建一个缓存列表
if(!this.peopleList[key]){
this.peopleList[key] = []
}
this.peopleList[key].push(fn)
console.log(this.peopleList[key])
}

Mymsg.listen('marrgie', name => {
console.log(`${name} 收到你结婚的消息了!`)
})
Mymsg.listen('unemployment', name => {
console.log(`${name} 收到你失业的消息了!`)
})

Mymsg.triger = function() {
let key = [].shift.call(arguments)
let fns = this.peopleList[key]

// 如果没有订阅则返回
if(!fns || fns.length === 0){
return false
}
for(let i = 0,fn;fn=fns[i++];i=fns.length) {
fn.apply(this,arguments)
}
}


Mymsg.triger('marrgie', 'Zhangsan') // Zhangsan 收到你结婚的消息了!
Mymsg.triger('unemployment', 'Lisi') // Lisi 收到你失业的消息了!
  • 对于发布消息这个功能,针对其他人都有这个需要,所以将它提取成一个公共的方法进行封装。
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
const event = {
peopleList : [],
listen : function(key,fn) {
if(!this.peopleList[key]){
this.peopleList[key] = []
}
this.peopleList[key].push(fn)
},
triger : function() {
let key = [].shift.call(arguments)
let fns = this.peopleList[key]

// 如果没有订阅则返回
if(!fns || fns.length === 0){
return false
}
for(let i = 0,fn;fn=fns[i++];i=fns.length) {
fn.apply(this,arguments)
}
}
}

const installEvent = function(obj) {
for(let i in event) {
obj[i] = event[i]
}
}

let Mymsg = {}

installEvent(Mymsg)

Mymsg.listen('marrgie', name => {
console.log(`${name} 收到你结婚的消息了!`)
})
Mymsg.listen('unemployment', name => {
console.log(`${name} 收到你失业的消息了!`)
})

Mymsg.triger('marrgie', 'Zhangsan')
Mymsg.triger('unemployment', 'Lisi')
  • 对于发布的消息,有时候订阅者也会取消订阅。比如:你和李四曾经是好朋友,因为一些事情决裂了,你将他从通讯列表中移除。所以在event中增加一个remove方法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
remove: function(key, fn) {
let fns = this.peopleList[key]
if(!fns) {
return false
}
if(!fn) {
fns && fns.length === 0
} else {
for(let i = 0;i<fns.length;i++) {
let _fn = fns[i]
if(_fn === fn) {
fns.splice(i, 1)
}
}
}
}

小结

  • 发布订阅者模式的优点在于时间上和对象之间的解耦,在架构上MVVM有这种模式的参与,Vue也是基于该模式的。
  • 而它的缺点在于,创建订阅者是需要消耗时间和内存的,并且订阅一个消息后,该消息最终并没有发生,而订阅者会始终存在于内存中。