生活中的发布订阅模式
小潘、小黄、小李都想要买部奥迪A4,一起去本市唯一一家4S店却被告知没有现车。4S店说:"如果您几位不着急的话,我们可以从上海给调车,这可能需要等上个几天(具体时间也不知道),几位方便的话留个联系方式,一到车我会第一时间通知各位"。于是这3人在登记表上了留下了联系方式一些配置要求...在一个礼拜过后,车到了,4S店根据登记表一一联系了小潘、小黄、小李前来取车。
发布订阅的作用
上面的例子中,这3位留下联系方式,4S店一一通知他们(一对多的关系)前来取车就是一个典型的发布订阅模式。小潘,小黄,小李都是订阅者,他们订阅了车子到店的信息,4S店作为发布者,会在车子到店发布信息给订阅者。
一步一步实现发布订阅
经过上面的例子,想必您对发布订阅模式或许有了些感官上的认识。如果还是觉得很模糊的话,那就跟着我一步一步来实现一个发布订阅。
动手之前的准备:
- 首先要指定发布者(就像4S店)
- 然后要给一个缓存列表,用于存放回调函数以便告知订阅者(就类似 登记表)
- 最后要发布消息的时候,发布者会遍历缓存列表,依次触发回调函数(4S店打登记表上的电话,通知他们前来取车)。
- 第一段代码
function Sale(){//指定发布者 定义一个4S店类
this._typeList = {};//定义缓存列表 存放回调函数
};
为什么把缓存列表定义成对象{}?
答:就像现实中的买车,不同的消费者会需要不同品牌的汽车,{'奥迪','大众','别克',....},4S店也经常
会遇到没有现车的情况,所以就需要登记不同品牌的需求。并且,许多购车人都想知道订购车的一些配置,
比如小潘订阅了一部奥迪,他在登记表中添加了一些他关心的配置(比如:动力,油耗,空间...)这些配置就
类似回调函数----奥迪:[动力,油耗,空间];等车子一到,4S店也会将相关配置告知小潘。2.第二段代码
Sale.prototype.on = function(type,fn){//指定并增加订阅者
if(!this._typeList[type]){//如果还没有订阅过此消息,就给他创建一个缓存列表 就像小潘
他们刚走,4S店来了一个人要迈巴赫,店里也没有现车,于是又在登记表上登记了
this._typeList[type] = [];
}
this._typeList[type].push(fn);
};3.第三段代码
Sale.prototype.emit = function(type,...arg){ //发布消息 车子一到店 就通知人取车
if(this._typeList[type]){//如果之前有过此订阅类型
this._typeList[type].forEach(item=>item(...arg))//执行回调
}else {//如果没有订阅 直接返回
return false
}
};4.前三段代码合并
function Sale(){
this._typeList = {};
};
Sale.prototype.on = function(type,fn){
if(!this._typeList[type]){
this._typeList[type] = [];
}
this._typeList[type].push(fn);
};
Sale.prototype.emit = function(type,...arg){
if(this._typeList[type]){
this._typeList[type].forEach(item=>item(...arg))
}else {
return false
}
};
var _4S = new Sale();
function fuel(who) { //小潘需要4s店在A4到车后,告知油耗
console.log('尊敬的'+who +'油耗=6.2-7.8L/100km');
}
function power(who) {//小潘需要4s店在A4到车后,告知其这车子的动力
console.log('尊敬的'+who+'发动机=2.0T');
}
_4S.on('A4',fuel)//购车者XXX向4S店订阅了 A4的消息
_4S.on('A4',power)
_4S.emit('A4','小潘')//4S店向小潘发布A4的消息突然不想要A4了
有时候,我们也许突然之间,不想再获得我们订阅的消息,这时候就需要设置取消订阅的功能。 小潘不想要A4了...
Sale.prototype.remove = function(type,fn){
var len = this._typeList[type];
if(!len){//如果这款品牌的车的没人订阅过 直接返回
return false
}
if(!fn){//如果小潘没有指定 4S店在A4到店后,要把哪些配置发给他(也即没有传入具体的回调函数),则表示取消type对应的所有订阅
len = null;
}else{
for(var i = len.length-1;i>=0;i--){
var _fn = len[i];
if(_fn === fn){
len.splice(i,1);//删除订阅的回调函数
}
}
}
}全部的代码
function Sale(){
this._typeList = {};
};
Sale.prototype.on = function(type,fn){
if(!this._typeList[type]){
this._typeList[type] = [];
}
this._typeList[type].push(fn);
};
Sale.prototype.emit = function(type,...arg){
if(this._typeList[type]){
this._typeList[type].forEach(item=>item(...arg))
}else {
return false
}
};
Sale.prototype.remove = function(type,fn){ var len = this._typeList[type];
if(!len){//如果这款品牌的车的没人订阅过 直接返回
return false
}
if(!fn){//如果小潘没有指定 4S店在A4到店后,要把哪些配置发给他(也即没有传入具体的回调函数),
则表示取消type对应的所有订阅
len.length = 0
}else{
for(var i = len.length-1;i>=0;i--){
var _fn = len[i];
if(_fn === fn){
len.splice(i,1);//删除订阅的回调函数
}
}
}
}
var _4S = new Sale();
function fuel(who) { //小潘需要4s店在A4到车后,告知油耗
console.log('尊敬的'+who +'油耗=6.2-7.8L/100km');
}
function power(who) {//小潘需要4s店在A4到车后,告知其这车子的动力
console.log('尊敬的'+who+'发动机=2.0T');
}
_4S.on('A4',fuel)//购车者XXX向4S店订阅了 A4的消息
_4S.on('A4',power)
_4S.on('A5',power)console.log(_4S._typeList); _4S.remove('A4',fuel)
_4S.emit('A4','小潘')//4S店向小潘发布A4的消息