发布订阅模式学习

414 阅读4分钟

生活中的发布订阅模式

小潘、小黄、小李都想要买部奥迪A4,一起去本市唯一一家4S店却被告知没有现车。4S店说:"如果您几位不着急的话,我们可以从上海给调车,这可能需要等上个几天(具体时间也不知道),几位方便的话留个联系方式,一到车我会第一时间通知各位"。于是这3人在登记表上了留下了联系方式一些配置要求...在一个礼拜过后,车到了,4S店根据登记表一一联系了小潘、小黄、小李前来取车。

发布订阅的作用

上面的例子中,这3位留下联系方式,4S店一一通知他们(一对多的关系)前来取车就是一个典型的发布订阅模式。小潘,小黄,小李都是订阅者,他们订阅了车子到店的信息,4S店作为发布者,会在车子到店发布信息给订阅者。

一步一步实现发布订阅

经过上面的例子,想必您对发布订阅模式或许有了些感官上的认识。如果还是觉得很模糊的话,那就跟着我一步一步来实现一个发布订阅。

动手之前的准备:

  • 首先要指定发布者(就像4S店)
  • 然后要给一个缓存列表,用于存放回调函数以便告知订阅者(就类似 登记表)
  • 最后要发布消息的时候,发布者会遍历缓存列表,依次触发回调函数(4S店打登记表上的电话,通知他们前来取车)。
  1. 第一段代码

 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的消息