现实举例:在某应用上预约了一个抢票活动,那么在抢票开始前的一段时间内,平台会自动将抢票活动通知重复发送给你,以提醒你不要忘记抢票。
在这里提到的某应用就是发布者,而报名活动的用户则是订阅者。
1.概念
定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都将得到通知。
2.实现发布订阅模式
首先要知道发布订阅模式的三大组成:
发布对象:事件发布者,将事件传给调度中心
调度中心:发布、订阅对象之间的中介,将订阅者与发布者联系起来
订阅对象:订阅事件,向调度中心发起订阅
step1
定义全局发布—订阅对象
作用:实现代码复用,减少订阅对象与发布对象的耦合度
const Event = (function () {
})();
step2
为全局对象添加相关方法和属性等
const Event = (function () {
//调度中心
const clientList = {};
//on方法,添加订阅
const on = function () {
};
//emit方法,发布事件
const emit = function () {
}
//off方法,取消订阅
const off = function () {
}
})();
on方法
接收两个参数,一个是键名(用来给事件增加唯一标识,订阅者只需传递唯一标识,然后发布者就可以根据唯一标识发布用户想要访问的事件了)一个是回调函数(订阅者想要访问的事件,比如抢票活动通知事件)
const on = function (key, callback) {
//没有唯一标识,就创建一个
if (!clientList[key]) {
clientList[key] = [];
}
//将回调传进数组中
clientList[key].push(callback);
};
emit方法
获取唯一标识所对应的事件,并执行获取到的事件
const emit = function () {
const key = Array.prototype.shift.call(arguments);
callbacks = clientList[key];
if (!callbacks || callbacks.length === 0) {
return false;
}
callbacks.forEach(callback => {
callback.apply(this, arguments);
});
}
off方法
如果没有传callback参数,则把key对应的值清空。否则,只删除callback
const off = function (key, callback) {
const callbacks = clientList[key];
if (!callbacks) {
return false;
}
if (!callback) {
callbacks && (callbacks.length = 0);
} else {
for (let i = 0; i < callbacks.length; i++) {
if (callbacks[i] === callback) {
callbacks.splice(i, 1);
}
}
}
};
完整版
const Event = (function () {
//调度中心
const clientList = {};
//on方法,添加订阅
const on = function (key, callback) {
if (!clientList[key]) {
clientList[key] = [];
}
clientList[key].push(callback);
};
//emit方法,发布事件
const emit = function () {
const key = Array.prototype.shift.call(arguments);
callbacks = clientList[key];
if (!callbacks || callbacks.length === 0) {
return false;
}
callbacks.forEach(callback => {
callback.apply(this, arguments);
});
};
//off方法,取消订阅
const off = function (key, callback) {
const callbacks = clientList[key];
if (!callbacks) {
return false;
}
if (!callback) {
callbacks && (callbacks.length = 0);
} else {
for (let i = 0; i < callbacks.length; i++) {
if (callbacks[i] === callback) {
callbacks.splice(i, 1);
}
}
}
};
return {
on,
emit,
off
}
})();
step3
使用,拿抢票活动举例,有两位用户订阅了抢票通知,如行1 行4,当临近抢票阶段(假设临近了),系统会自动发布对应事件给当事人,如行7 行8。但是用户2023在20小时后又有其它安排了,不得不取消这次预约,如行9。
Event.on('用户2022', fn1 = function (information) {
console.log(information);
})
Event.on('用户2023', fn2 = function (information) {
console.log(information);
})
Event.emit('用户2022', '您预约的公益讲座抢票活动将在10分钟后开启');
Event.emit('用户2023', '您预约的小型演唱会(主楼)抢票活动将在20小时后开启');
Event.off('用户2023', fn2);
结尾:
发布-订阅模式优点:
对象间的解耦,发布者与订阅者完全不需要事先知道对方的存在,只需要Event将它们连接起来即可
发布-订阅缺点:
创建订阅者要占用一定的内存,也可能出现,虽然订阅了,但是事件最后都没有发生的情况。
过度使用的话,对象间(发布者与订阅者)的联系也不好梳理,后续会出现难以维护、理解的情况。
问题:
观察者模式与发布-订阅模式的区别:
发布订阅模式其实属于广义上的观察者模式。
在观察者模式中,观察者需要直接订阅目标事件。在目标发出内容改变的事件后,直接接收事件并作出响应。
而在发布订阅模式中,发布者和订阅者之间多了一个调度中心。调度中心一方面从发布者接收事件,另一方面向订阅者发布事件,订阅者需要在调度中心中订阅事件。通过调度中心实现了发布者和订阅者关系的解耦。使用发布订阅者模式更利于我们代码的可维护性。