觀察者模式(observer):又被稱為 發布-訂閱者模式或者消息機制,定義了一種依賴關系,解決了主體對象與觀察者之間功能耦合。
一、這樣的需求
在實現自己的需求,而添加一些功能代碼,但是又不想新添加的代碼影響他人的實現功能,也就是說,你不想讓自己的模塊與他人的模塊嚴重耦合在一起,對於這類問題,觀察者模式是比較理想的解決方案。
觀察者模式可以解開我和他們之間的功能耦合。
觀察者模式,也被人稱為消息機制或者發布-訂閱者模式。為了解決主體對象與觀察者之間功能耦合
二、創建一個觀察者
把觀察者或者消息系統看作一個對象,那么他應該包含兩個方法,一個是接受消息,一個是向中轉站發送相應消息。
首先,我們把需要的把觀察者對象創建出來,有一個消息容器和三個方法,分別是,訂閱消息的方法,取消訂閱消息的方法,發送訂閱消息的方法。
var Observer = (function(){ //防止消息隊列暴露而被篡改,所以將消息容器作為靜態私有變量保存 var __messsages = {}; return { //注冊信息接口 regist: function(){}, //發布信息的接口 fire: function(){}, //移除信息接口 remove: function(){} } })();
觀察者對象的雛形出來了,我們需要做的事情就是實現這三個方法,我們首先實現消息注冊方法,注冊方法的作用是將訂閱者注冊的消息推入到消息隊列中,因此我們需要接受兩個參數:消息類型和以及相應的處理動作,在推入到消息隊列時如果此消息不存在應該創建一個該消息類型並將該消息放入到消息隊列中,如果此消息存在則應該將消息執行方法推入該消息對應的執行方法隊列中,這么做目的是保證多個模塊注冊同一個消息能順利執行。
regist: function(type,fn){ //如果此消息不存在,則應該創建一個該消息類型 if(typeof __messages[type] === 'undefined'){ //將對象推入到該消息對應的動作執行隊列中 __messages[type] = [fn]; //如果此消息存在 }else{ //將動作方法推入該消息對應的動作執行序列中 __messages[type].push(fn); } }
對於發布消息方法,其功能是當觀察者發布一個消息時將所有訂閱者訂閱的消息一次執行。
故應該接受兩個參數,消息類型以及動作執行時需要傳遞的參數,當然在這里消息類型是必須的。在執行消息隊列之前校驗消息的存在是很有必要的。
然后遍歷消息執行方法隊列,並依此執行。
然后將消息類別以及傳遞的參數打包后依次傳入消息隊列執行方法中。
fire: function(type,args){ //如果該消息沒有被注冊,則返回 if(!__messages[type]){ return ; //定義消息信息 var events = { type:type, args:args||{} }, i=0; len = __messages[type].length; for(;i<len;i++){ //依次執行注冊的消息對應的動作序列 __messages[type][i].call(this,events); } }
最后是消息注冊方法,其功能是將訂閱者注銷的消息從消息隊列清除,因此我們也需要兩個參數,即消息類型以及執行的某一個動作。當然為了避免刪除消息動作時消息不存在情況的出現,對消息隊列中消息的存在性校驗也很有必要的。
remove:function(type,fn){ //如果消息動作隊列存在 if(__messages[type] instanceof Array){ //從最后一個消息動作遍歷 var i = __messages[type].length-1; for(;i>=0;i--){ //如果存在該動作則在消息動作中移除相應動作 __messages[type][i] === fn && __messages[type].splice(i,1); } } }
三、測試一下
觀察者對象或者說,消息系統創建成功之后,簡單測試一下:
首先訂閱一條消息
Observer.regist('test',function(e){
console.log(e.type,e.args.msg);
});
然后,我們發布這則消息
Observer.fire('test',{msg:'傳遞參數'}); //test 傳遞參數
四、回憶一下
觀察者模式最主要的作用的:解決類或對象之間的耦合,解耦兩個相互依賴的對象,使其依賴於觀察者的消息機制。
這樣對於任意一個訂閱者對象來說,其他訂閱者對象的改變不會影響到自身。
對於每一個訂閱者來說,其自身既可以是消息的發出者也可以是消息的執行者,這都依賴於調用觀察者對象的三個方法(訂閱消息,注銷消息,發布消息)
