看到过一句话,如果只选择一种设计模式去学习,你要选哪种,答案就是”发布-订阅“模式,由此可见这种模式的重要性。

什么是发布-订阅?

发布-订阅,就是一种一对多或者多对多的信息传递方式。

现实中最好的例子就是”微信公众号“了,公众号只有一个,但关注者可以有很多,公众号不需要关心关注者是谁,关注者也不需要经常去找公众号要信息,只要二者的关系确定,公众号发布消息,关注者就能收到。

特点决定应用

通过上面的描述,可以发现这种模式有什么特点呢?

  • 异步:关注者不需要时刻盯着,只要公众号发了消息,就能随时接收
  • 松耦合:公众号改名字了,换人了,都不会影响关注者接收信息

第一点,说明可以应用到异步编程中,比如替代回调函数,而第二点,可以取代对象之间硬编码的通知机制,一个对象可以不再显式地调用另一个对象的接口,互相之间只要关系没变,其他的改变了也没有影响。

如何实现

说的这么厉害,怎么实现呢?可分为如下几步:

  • 指定发布者
  • 给发布者添加缓存列表,用于存放回调函数,以通知订阅者
  • 发布者遍历缓存列表,依次触发存放的订阅者回调

当然,既然是通知,就不能是空的,它应该可以传递一些参数作为信息给到订阅者。

代码时间:

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
var event = {
clientList:[],
//订阅函数接受订阅者标识和回调函数
listen:function(key,fn){
if(!this.clientList[key]){
this.clientList[key] = [];
}
this.clientList[key].push(fn);
},
//触发消息通知
trigger:function(){
var key = Array.prototype.shift.call(arguments),
fns = this.clientList[key];
if(!fns || fns.length === 0){
return false;
}
for(var i = 0,fn;fn = fns[i++];){
fn.apply(this,arguments)
}
}
}

//赋予发布者订阅的能力
var installEvent = function(obj){
for(var i in event){
obj[i] = event[i];
}
}

//取消订阅
event.remove = function(key,fn){
var fns = this.clientList[key];
if(!fns){
return false;
}
if(!fn){
fns && (fns.length = 0);
}else{
for(var len = fns.length-1;len >= 0;len--){
var _fn = fns[len];
if(_fn === fn){
fns.splice(1,1);
}
}
}
}

var public = {};
installEvent(public);
public.listen('a',function(article){
console.log('小明订阅了' + article);
});
public.listen('b',function(article){
console.log('小红订阅了' + article);
});

public.trigger("a","《JavaScript开发大全》");
public.trigger("b","《CSS设计指南》");