发布于 

设计模式之发布订阅模式

发布-订阅或观察者模式:定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都将得到通知。

介绍软件架构中,发布-订阅模式是一种消息范式,消息的发送者(称为发布者)不会将消息直接发送给特定的接收者(称为订阅者)。而是将发布的消息分为不同的类别,无需了解哪些订阅者(如果有的话)可能存在。同样的,订阅者可以表达对一个或多个类别的兴趣,只接收感兴趣的消息,无需了解哪些发布者(如果有的话)存在。

应用场景

假设小明要买房,小明未必要亲自去售楼处了解具体情况,只需要把订阅的请求交给中介公司,而各大房产公司也只需要通过中介公司来发布房子信息,这样小明就可以收到发布者发布的信息。在整个过程中,小明(订阅者)与房产公司(发布者)通信是通过中介公司完成的,不用关心是谁订阅、也不需要知道谁发布。像这种情况就可以使用发布-订阅模式

发布-订阅模式可以用一个全局的Event对象(如:中介公司)来实现,订阅者不需要了解消息来自哪个发布者,发布者也不知道消息会推送给哪些订阅者,Event作为一个类似“中介者”的角色,把订阅者和发布者联系起来

具体代码如下:

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
80
interface ISubscriber{
name:string;
eventName:string;
callback:(info:string)=>void
}
interface IPublisher{
name:string;
data:{
message:string
}
}
//订阅者类
class Subscriber implements ISubscriber{
name:string; //订阅者名称
eventName:string; //订阅者事件
callback:(info?:string)=>void; //发布后的操作
constructor(name:string,eventName:string,callback:(info?:string)=>void){
this.name = name;
this.eventName = eventName;
this.callback = callback
}
}
//发布者类
class Publisher implements IPublisher{
name:string; //发布的事件名
data:{message:string}; //发布的数据
constructor(name:string,data:{message:string}){
this.name = name;
this.data = data
}
}
//事件总线
class EventBus{
subscribeList:any = {} //缓存对象:每个事件都是一个数组,数组里面保存订阅对象信息
//添加
on(subObj:ISubscriber){
console.log(`开始订阅${subObj.eventName}事件`,subObj.name)
if(!this.subscribeList[subObj.eventName]){ //如果不存在就新增一个数组进行缓存
this.subscribeList[subObj.eventName] = []
}
this.subscribeList[subObj.eventName].push({name:subObj.name,callback:subObj.callback})
}
//触发
emit(pubObj:IPublisher){
console.log('正在触发',pubObj.name); //发布消息后触发对应的订阅者
this.subscribeList[pubObj.name].forEach((item:any)=>{
item.callback(pubObj.data.message)
})
}
//取消
off(eventName:string,obj:ISubscriber){ //取消订阅者订阅
console.log(`*****正在取消${eventName}*****`);
this.subscribeList[eventName] = this.subscribeList[eventName].filter((item:any)=>{
return item.name!=obj.name
})
}
}


const xiaoming = new Subscriber('xiaoming','squareMeter100',(info)=>{
console.log('xiaoming订阅了squareMeter100事件',info!)
})
const haha = new Subscriber('qingzi','squareMeter100',(info)=>{
console.log('qingzi订阅了squareMeter100事件',info)
})
const xbb = new Subscriber('xbb','squareMeter300',(info)=>{
console.log('xbb订阅了squareMeter300该事件',info)
})
const eventbus = new EventBus()
const p1 = new Publisher('squareMeter100',{message:'100平米的房子'})
const p2 = new Publisher('squareMeter300',{message:'300平米的大房子'})
eventbus.on(xiaoming);
eventbus.on(haha);
eventbus.on(xbb);
//发布“squareMeter100”相关消息
eventbus.emit(p1);
//发布“squareMeter300”相关消息
eventbus.emit(p2);
//取消小明订阅的squareMeter100事件
eventbus.off('squareMeter100',xiaoming);

上述代码中一个订阅者只能订阅一个消息,修改代码,让一个订阅者可订阅多个消息

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
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
//一个订阅者可订阅多个消息
type subEventListT = {eventName:string,callback:(info:string)=>void}
interface ISubscriber{
name:string;
subEventList:subEventListT[]
addSubEvent:(eventName:string,callback:()=>void)=>void;
offSubEvent:(eventName:string)=>void
}
interface IPublisher{
name:string;
data:{
message:string
}
}
//订阅者类
class Subscriber implements ISubscriber{
name:string; //订阅者名称
subEventList:subEventListT[]
constructor(name:string){
this.name = name;
this.subEventList = []
}
//增加订阅事件,可订阅多个
addSubEvent(eventName:string,callback:(info:string)=>void){
this.subEventList.push({eventName:eventName,callback:callback})
// console.log(this.subEventList)
}
//取消订阅事件
offSubEvent(eventName:string){
this.subEventList = this.subEventList.filter((item:subEventListT)=>{
return item.eventName!=eventName
})
console.log(`已取消${this.name}${eventName}事件`,this.subEventList)
}
}
//发布者类
class Publisher implements IPublisher{
name:string; //发布的事件名
data:{message:string}; //发布的数据
constructor(name:string,data:{message:string}){
this.name = name;
this.data = data
}
}
//事件总线
class EventBus{
subscribeList:any = {} //缓存对象:每个事件都是一个数组,数组里面保存订阅对象信息
//添加
on(subObj:ISubscriber){
console.log('开始订阅');
subObj.subEventList.forEach((item:subEventListT)=>{
if(!this.subscribeList[item.eventName]){
this.subscribeList[item.eventName] = []
}
//添加一个对象,增加name标识是谁订阅的
this.subscribeList[item.eventName].push({name:subObj.name,callback:item.callback})
})
// console.log(this.subscribeList)
}
//触发
emit(pubObj:IPublisher){
console.log('正在触发',pubObj.name); //发布消息后触发对应的订阅者
this.subscribeList[pubObj.name].forEach((item:any)=>{
item.callback(pubObj.data.message)
})
}
//取消
off(eventName:string,obj:ISubscriber){ //取消订阅者订阅
//通知订阅者取消某事件的订阅
obj.offSubEvent(eventName)
//取消缓存数组的订阅者信息
this.subscribeList[eventName] = this.subscribeList[eventName].filter((item:any)=>{
return item.name!=obj.name
})
console.log(this.subscribeList)
}
}

const xiaoming = new Subscriber('xiaoming')
xiaoming.addSubEvent('squareMeter100',(info)=>{
console.log('xiaoming添加了squareMeter100订阅',info)
})

xiaoming.addSubEvent('squareMeter200',(info)=>{
console.log('xiaoming添加了squareMeter200订阅',info)
})
const hehe = new Subscriber('hehe');
hehe.addSubEvent('squareMeter100',(info)=>{
console.log('hehe也订阅squareMeter100',info)
})
xiaoming.addSubEvent('squareMeter300',(info)=>{
console.log('xiaoming添加了squareMeter300订阅',info)
})
hehe.addSubEvent('squareMeter300',(info)=>{
console.log('hehe也订阅squareMeter300',info)
})
const eventbus = new EventBus()
eventbus.on(xiaoming)
eventbus.on(hehe)

const p = new Publisher('squareMeter100',{message:'100平米的房子'})
eventbus.emit(p)

eventbus.off('squareMeter100',xiaoming)

const p2 = new Publisher('squareMeter300',{message:'300平米的大房子'});
eventbus.emit(p2)