美文网首页
发布-订阅模式

发布-订阅模式

作者: QinRenMin | 来源:发表于2019-06-04 15:38 被阅读0次
    • 定义
      发布—订阅模式又叫观察者模式,它定义对象间的一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都将得到通知。
    • 步骤
      下面是实现发布—订阅模式的步骤:

    1、先要指定好谁充当发布者(比如售楼处)

    2、然后给发布者添加一个缓存列表,用于存放回调函数以便通知订阅者(售楼处的花名册)

    3、最后发布消息的时候,发布者会遍历这个缓存列表,依次触发里面存放的订阅者回调函数(遍历花名册,挨个发短信)

    另外,还可以往回调函数里填入一些参数,订阅者可以接收这些参数。这是很有必要的,比如售楼处可以在发给订阅者的短信里加上房子的单价、面积、容积率等信息,订阅者接收到这些信息之后可以进行各自的处理

    var salesOffices = {}; // 定义售楼处
    salesOffices.clientList = []; // 缓存列表,存放订阅者的回调函数
    salesOffices.listen = function( fn ){ // 增加订阅者
        this.clientList.push( fn ); // 订阅的消息添加进缓存列表
    };
    salesOffices.trigger = function(){ // 发布消息
        for( var i = 0, fn; fn = this.clientList[ i++ ]; ){
            fn.apply( this, arguments ); // (2) // arguments 是发布消息时带上的参数
        }
    };
    salesOffices.listen( function( price, squareMeter ){ // 小明订阅消息
        console.log( '价格= ' + price );
        console.log( 'squareMeter= ' + squareMeter );
    });
    
    salesOffices.listen( function( price, squareMeter ){ // 小红订阅消息
        console.log( '价格= ' + price );
        console.log( 'squareMeter= ' + squareMeter );
    });
    
    salesOffices.trigger( 2000000, 88 ); // 输出:200 万,88 平方米
    salesOffices.trigger( 3000000, 110 ); // 输出:300 万,110 平方米
    

    但这里还存在一些问题。看到订阅者接收到了发布者发布的每个消息,虽然小明只想买88平方米的房子,但是发布者把110平方米的信息也推送给了小明,这对小明来说是不必要的困扰。所以有必要增加一个标示key,让订阅者只订阅自己感兴趣的消息。改写后的代码如下:

    var salesOffices = {}; // 定义售楼处
    salesOffices.clientList = []; // 缓存列表,存放订阅者的回调函数
    
    salesOffices.listen = function( key, fn ){
        if ( !this.clientList[ key ] ){ // 如果还没有订阅过此类消息,给该类消息创建一个缓存列表
            this.clientList[ key ] = [];
        }
        this.clientList[ key ].push( fn ); // 订阅的消息添加进消息缓存列表
    };
    
    salesOffices.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 ); // (2) // arguments 是发布消息时附送的参数
        }
    };
    
    salesOffices.listen( 'squareMeter88', function( price ){ // 小明订阅88 平方米房子的消息
        console.log( '价格= ' + price ); // 输出: 2000000
    });
    
    salesOffices.listen( 'squareMeter110', function( price ){ // 小红订阅110 平方米房子的消息
        console.log( '价格= ' + price ); // 输出: 3000000
    });
    
    salesOffices.trigger( 'squareMeter88', 2000000 ); // 发布88 平方米房子的价格
    salesOffices.trigger( 'squareMeter110', 3000000 ); // 发布110 平方米房子的价格
    
    • 通用实现
    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 ), // (1);
                fns = this.clientList[ key ];
                if ( !fns || fns.length === 0 ){ // 如果没有绑定对应的消息
                    return false;
                }
                for( var i = 0, fn; fn = fns[ i++ ]; ){
                    fn.apply( this, arguments ); // (2) // arguments 是trigger 时带上的参数
                }
            }
        };
    

    再定义一个installEvent函数,这个函数可以给所有的对象都动态安装发布—订阅功能:

    var installEvent = function( obj ){
        for ( var i in event ){
            obj[ i ] = event[ i ];
        }
    };
    

    下面给售楼处对象salesOffices动态增加发布—订阅功能

    var salesOffices = {};
    installEvent( salesOffices );
    salesOffices.listen( 'squareMeter88', function( price ){ // 小明订阅消息
        console.log( '价格= ' + price );
    });
    salesOffices.listen( 'squareMeter100', function( price ){ // 小红订阅消息
        console.log( '价格= ' + price );
    });
    salesOffices.trigger( 'squareMeter88', 2000000 ); // 输出:2000000
    salesOffices.trigger( 'squareMeter100', 3000000 ); // 输出:3000000
    
    • 取消订阅事件
      有时候,也许需要取消订阅事件的功能。比如小明突然不想买房子了,为了避免继续接收到售楼处推送过来的短信,小明需要取消之前订阅的事件。现在给event对象增加remove方法
    event.remove = function( key, fn ){
        var fns = this.clientList[ key ];
        if ( !fns ){ // 如果key 对应的消息没有被人订阅,则直接返回
            return false;
        }
        if ( !fn ){ // 如果没有传入具体的回调函数,表示需要取消key 对应消息的所有订阅
            fns && ( fns.length = 0 );
        }else{
            for ( var l = fns.length - 1; l >=0; l-- ){ // 反向遍历订阅的回调函数列表
                var _fn = fns[ l ];
                if ( _fn === fn ){
                    fns.splice( l, 1 ); // 删除订阅者的回调函数
                }
            }
        }
    };
    var salesOffices = {};
    var installEvent = function( obj ){
        for ( var i in event ){
            obj[ i ] = event[ i ];
        }
    }
    installEvent( salesOffices );
    
    salesOffices.listen( 'squareMeter88', fn1 = function( price ){ // 小明订阅消息
        console.log( '价格= ' + price );
    });
    
    salesOffices.listen( 'squareMeter88', fn2 = function( price ){ // 小红订阅消息
        console.log( '价格= ' + price );
    });
    
    salesOffices.remove( 'squareMeter88', fn1 ); // 删除小明的订阅
    salesOffices.trigger( 'squareMeter88', 2000000 ); // 输出:2000000
    

    相关文章

      网友评论

          本文标题:发布-订阅模式

          本文链接:https://www.haomeiwen.com/subject/kezlxctx.html