js设计模式之发布订阅模式

什么是发布订阅模式

发布订阅模式又叫观察者模式,定义的是对象间一对多的依赖关系。当对象A的状态发生改变时,所有依赖于对象A的其他对象都会收到通知,并触发他们各自的回调函数。其中,对象A就是发布者,其他对象就是订阅者,类似于A像其他对象推送消息。JS中的事件机制就是发布订阅者模式的体现。

发布订阅模式的特点

优点:(1)时间上的解耦

          (2)对象上的解耦

缺点:(1)创建订阅者要消耗时间和内存,就算订阅的消息一直不发生,订阅者也会一直存在于内存中;

          (2)过渡使用会导致对象之间的关系过于弱化,程序难以跟踪维护。

发布订阅模式的实现步骤

(1)指定一个充当发布者的对象,可以是某个详细的对象,也可以是全局对象(作为中间代理)

(2)给发布者添加缓存列表,用于存放回调函数,以便通知订阅者。

(3)发布消息时,会遍历缓存列表中对应key值得数组,触发里面的回调函数的执行。

通用发布订阅模式

/**发布订阅模式的通用实现 */
//将发布订阅功能单独提出来,放在一个对象内
var event = {
    clientList: {},
    listen: function(key,fn){
        if(!this.clientList[key]){
            this.clientList[key] = [];
        }
        this.clientList[key].push(fn);
    },
    trigger: function(){
        var argus = Array.prototype.slice.call(arguments,0),
            key = argus[0],
            fns = this.clientList[key];
        if(!fns || fns.length==0){
            return false;
        }
        for(var i=0;i<fns.length;i++){
            fns[i].apply(this,argus.slice(1));
        }
    },
    // 要想能够删除fn回调函数,传进来的回调函数必须是具名的
    remove: function(key,fn){
        var fns = this.clientList[key];

        if(!fns){
            return false;
        }
        if(!fn){
            fns && (fns.length = 0);
        }else{
            for(var j=fns.length-1;j>=0;j--){
                if(fn === fns[j]){
                    fns.splice(j,1);
                }
            }
        }
    } 
}

//给对象安装发布订阅功能的函数
var installEvent = function(obj){
    for(var i in event){
        obj[i] = event[i];
    }
}

var saleOffice = {};
installEvent(saleOffice);

//测试
saleOffice.listen('square88',fn1 = function(price){
    console.log('价格为:'+ price);
})//小明的订阅

saleOffice.listen('square110',fn2 = function(price){
    console.log("price="+ price);
})//小红的订阅

saleOffice.listen('square88',fn3 = function(price){
    console.log('面积为:'+ '88');
})

saleOffice.trigger('square88',1000000);
saleOffice.trigger('square110',3000000);

saleOffice.remove('square110',fn2);
saleOffice.trigger('square110',3000000);

全局发布订阅模式

/**全局发布订阅对象 */
// 避免不断的创建发布对象和缓存列表,节约空间;形成一个中介,发布者和订阅者之间解耦
var Event = (function(){
    var clientList = {},
        listen,
        trigger,
        remove;
    
    listen = function(key,fn){
        if(!clientList[key]){
            clientList[key] = [];
        }
        clientList[key].push(fn);
    };
    trigger = function(){
        var argu = Array.prototype.slice.call(arguments,0),
            key = argu[0];
            fns = clientList[key];
        if(!fns || fns.length == 0){
            return false;
        }
        for(var i=0;i<fns.length;i++){
            fns[i].apply(this,argu.slice(1));
        }
    };
    remove = function(key,fn){
        var fns = clientList[key];
        if(!fns){
            return false;
        }
        if(!fn){
            fns && (fns.length=0);
        }else{
            for(var j=fns.length-1;j>=0;j--){
                if(fn===fns[j]){
                    fns.splice(j,1);
                }
            }
        }
    };

    return {
        listen: listen,
        trigger: trigger,
        remove: remove
    }
})()

//测试
Event.listen('square88',fn1 = function(price){
    console.log('价格为:'+ price);
})//小明的订阅

Event.listen('square110',fn2 = function(price){
    console.log("price="+ price);
})//小红的订阅

Event.listen('square88',fn3 = function(price){
    console.log('面积为:'+ '88');
})

Event.trigger('square88',1000000);
Event.trigger('square110',3000000);

Event.remove('square110',fn2);
Event.trigger('square110',3000000);

全局发布订阅者模式(1)节约资源;避免为每个对象添加listen,trigger方法,以及缓存列表;(2)解除了耦合;由之前的通用发布订阅模式可以知道,订阅者需要知道发布对象的名字才能订阅事件;通过一个全局的中介,我们无需关心谁是发布者,谁是订阅者,这样有利于完成模块间的通信。

猜你喜欢

转载自blog.csdn.net/qq_35585701/article/details/79888394