發佈-訂閱模式

簡介

發佈-訂閱模式又叫觀察者模式,它定義對象間的一種一對多的依賴關係,當一個對象的狀態發生改變時,全部依賴於它的對象都將獲得通知。JavaScript開發中咱們通常用事件模型來代替傳統的發佈-訂閱模式前端

案例介紹1

小明最近喜歡上吃老北京燒餅,但是到了賣燒餅的地方發現已經賣完了,並且排隊的人還不少.幸運的是賣燒餅那個MM看小明長得帥,告訴小明等一會就有燒餅吃啦!但是小明如今還有約會要去,不知道燒餅能何時出鍋,總不能由於吃燒餅而不去約會吧!這時候小明靈機一動,說燒餅MM把你電話給我吧!我先去忙,等會打電話問你燒餅好了沒有。燒餅MM也沒想太多,把電話給小明瞭。後來小龍也來買燒餅,狀況跟小明差很少,小龍也把燒餅MM的電話要走了。但是問題就這來了,小明、小龍一下子打一個電話給燒餅MM,致使燒餅MM很煩,辭職走了不幹了。
經過上邊的事情咱們能夠發現,存在好多問題
第一:賣燒餅的MM應該充當發佈者
第二:小明小龍的電話應該保存在賣燒餅的用戶列表中,若是賣燒餅的MM離職,這用戶就會丟失
第三:實際上沒有這麼笨蛋的銷售方式的編程

賣燒餅的店主能夠把小明、小龍的電話記錄下來,等店裏有燒餅了在通知小龍小明來拿這就是所謂的發佈-訂閱模式,代碼以下:設計模式

/*燒餅店*/        
var Sesamecakeshop={
    clienlist:[],//緩存列表
    addlisten:function(fn){//增長訂閱者
        this.clienlist.push(fn);
    },
    trigger:function(){//發佈消息
        for(var i=0,fn;fn=this.clienlist[i++];){
            fn.apply(this,arguments);
        }
    }
}

/*小明發布訂閱*/
Sesamecakeshop.addlisten(function(price,taste){
    console.log("小明發布的"+price+"元,"+taste+"味道的");
});
/*小龍發佈訂閱*/
Sesamecakeshop.addlisten(function(price,taste){
    console.log("小龍發佈的"+price+"元,"+taste+"味道的");
});        

Sesamecakeshop.trigger(10,"椒鹽");複製代碼

從代碼中能夠看出,只有小明,小龍預約了燒餅,燒餅店就能夠發佈消息告訴小龍與小明。可是有個問題不知道你們發現了沒有。小明只喜歡椒鹽味道的。而小龍只喜歡焦糖味道的。上面的代碼就知足不了客戶的需求,給客戶一種感受就是,無論我喜歡不喜歡,你都會發給我。若是發佈比較多,客戶就會感到厭煩,甚至會想刪除訂閱。下邊是對代碼進行改良你們能夠看看。緩存

/*燒餅店*/        
var Sesamecakeshop={
    clienlist:{},/*緩存列表*/
    /**
     * 增長訂閱者
     * @key {String} 類型
     * @fn {Function} 回掉函數
     * */
    addlisten:function(key,fn){
        if(!this.clienlist[key]){
            this.clienlist[key]=[];
        }
        this.clienlist[key].push(fn);
    },
    /**
     * 發佈消息
     * */
    trigger:function(){
        var key=[].shift.call(arguments),//取出消息類型
            fns=this.clienlist[key];//取出該類型的對應的消息集合
        if(!fns || fns.length===0){
            return false;
        }
        for(var i=0,fn;fn=fns[i++];){
            fn.apply(this,arguments);
        }
    },
    /**
     * 刪除訂閱
     * @key {String} 類型
     * @fn {Function} 回掉函數
     * */
    remove:function(key,fn){
        var fns=this.clienlist[key];//取出該類型的對應的消息集合
        if(!fns){//若是對應的key沒有訂閱直接返回
            return false;
        }
        if(!fn){//若是沒有傳入具體的回掉,則表示須要取消全部訂閱
            fns && (fns.length=0);
        }else{
            for(var l=fns.length-1;l>=0;l--){//遍歷回掉函數列表
                if(fn===fns[l]){
                    fns.splice(l,1);//刪除訂閱者的回掉
                }
            }
        }
    }
}

/*小明發布訂閱*/
Sesamecakeshop.addlisten("焦糖",fn1=function(price,taste){
    console.log("小明發布的"+price+"元,"+taste+"味道的");
});
/*小龍發佈訂閱*/
Sesamecakeshop.addlisten("椒鹽",function(price,taste){
    console.log("小龍發佈的"+price+"元,"+taste+"味道的");
});        

Sesamecakeshop.trigger("椒鹽",10,"椒鹽");

Sesamecakeshop.remove("焦糖",fn1);//注意這裏是按照地址引用的。若是傳入匿名函數則刪除不了        

Sesamecakeshop.trigger("焦糖",40,"焦糖");複製代碼

刪除的時候須要注意的是,若是訂閱的時候傳遞的是匿名函數,刪除的時候若是傳入的也是匿名函數。則刪除不了。由於刪除時候是按照地址引用刪除的。傳進去的兩個匿名函數,對應的地址引用是不一樣的。bash

案例介紹2

好比我們常見的用戶身份分別有不一樣的功能,超級管理員擁有最高權限,能夠刪除修改任意用戶。而普通用戶則只能修改本身的帳戶信息。首先是用戶身份驗證,驗證經過以後對應功能才能夠顯示。前端工程師

//登陸發佈-訂閱模式
login={
    clienlist:{},/*緩存列表*/
    /**
     * 增長訂閱者
     * @key {String} 類型
     * @fn {Function} 回掉函數
     * */
    addlisten:function(key,fn){
        if(!this.clienlist[key]){
            this.clienlist[key]=[];
        }
        this.clienlist[key].push(fn);
    },
    /**
     * 發佈消息
     * */
    trigger:function(){
        var key=[].shift.call(arguments),//取出消息類型
            fns=this.clienlist[key];//取出該類型的對應的消息集合
        if(!fns || fns.length===0){
            return false;
        }
        for(var i=0,fn;fn=fns[i++];){
            fn.apply(this,arguments);
        }
    }
}
//超級管理員修改全部用戶
var editall=(function(){
    login.addlisten("loginsucc",function(data){
        editall.setview(data);
    });
    return{
        setview:function(data){
            console.log(data);
            console.log("超級管理員修改全部用戶");
        }
    }
})();

//僅僅修改本身
var editOwn=(function(){
    login.addlisten("loginsucc",function(data){
        editOwn.setview(data);
    });
    return{
        setview:function(data){
            console.log(data);
            console.log("僅僅修改本身");
        }
    }
})();複製代碼

發佈-訂閱模式簡單封裝架構

var _Event=(function(){
    var clienlist={},
    addlisten,trigger,remove;
    /**
     * 增長訂閱者
     * @key {String} 類型
     * @fn {Function} 回掉函數
     * */
    addlisten=function(key,fn){
        if(!clienlist[key]){
            clienlist[key]=[];
        }
        clienlist[key].push(fn);
    };
    /**
     * 發佈消息
     * */
    trigger=function(){
        var key=[].shift.call(arguments),//取出消息類型
            fns=clienlist[key];//取出該類型的對應的消息集合
        if(!fns || fns.length===0){
            return false;
        }
        for(var i=0,fn;fn=fns[i++];){
            fn.apply(this,arguments);
        }
    };
    /**
     * 刪除訂閱
     * @key {String} 類型
     * @fn {Function} 回掉函數
     * */
    remove=function(key,fn){
        var fns=clienlist[key];//取出該類型的對應的消息集合
        if(!fns){//若是對應的key沒有訂閱直接返回
            return false;
        }
        if(!fn){//若是沒有傳入具體的回掉,則表示須要取消全部訂閱
            fns && (fns.length=0);
        }else{
            for(var l=fns.length-1;l>=0;l--){//遍歷回掉函數列表
                if(fn===fns[l]){
                    fns.splice(l,1);//刪除訂閱者的回掉
                }
            }
        }
    };
    return{
        addlisten:addlisten,
        trigger:trigger,
        remove:remove
    }
})();


_Event.addlisten("jianbing",function(d,all){
    console.log("發佈的消息來自:"+d+",具體信息:"+all);
});
_Event.addlisten("jianbing",function(d,all){
    console.log("發佈的消息來自:"+d+",具體信息:"+all);
})
_Event.trigger("jianbing","小小坤","前端工程師,擅長JavaScript,喜歡結交更多的前端技術人員,歡迎喜歡技術的你加QQ羣:198303871")複製代碼

總結

發佈-訂閱模式就是常說的觀察者模式,在實際開發中很是有用。它的優勢是爲時間是解耦,爲對象之間解構,它的應用很是普遍,既能夠在異步編程中也能夠幫助咱們完成更鬆的解耦。發佈-訂閱模式還能夠幫助咱們實現設計模式,從架構上來看,不管MVC仍是MVVC都少不了發佈-訂閱模式的參與。然而發佈-訂閱模式也存在一些缺點,建立訂閱自己會消耗必定的時間與內存,也許當你訂閱一個消息以後,以後可能就不會發生。發佈-訂閱模式雖然它弱化了對象與對象之間的關係,可是若是過分使用,對象與對象的必要聯繫就會被深埋,會致使程序難以跟蹤與維護。app

相關文章
相關標籤/搜索