職責鏈模式其實很好理解,因爲一個鏈字出賣了它的靈魂。咱們能夠從這個字獲得很大的提示。首先這個模式必定有傳遞性,並且,節點是能夠重複拼接的,而且每一個節點都具備必定的過濾功能,必定的職責。編程
是否是想起了組合模式裏的一些內容呢? 是的,他們兩個有着自然的相似點,不過組合模式主要職責是給執行者添加一些列行爲,而不區份內部的執行。職責鏈模式則會強調內部的細節,他能夠手動傳遞權限,手動終止權限。設計模式
舉個栗子吧:app
小時候,俺是一個學渣,平時做業都不會作,可是老師硬性要求你作。沒辦法,只有去借鑑學霸的做業。首先,咱們班人都超級好,我作在最後一排,而後函數
我問前一排的妹紙: 嗨,小芳,你的做業能給我看看嘛? 小芳: 我做業沒作呢?我幫你問問前面的。 小芳: 小明,你做業作完了嗎?能給我看看嘛? 小明: 我做業沒作呢?我幫你問問前面的。 小明: 小吉,你做業作完了嗎?能給我看看嘛? 小吉: 作完了,給你吧。
恩,good,事情圓滿解決。完美的體現出,職責鏈的內涵,上一節點,只要知道下一個節點的接口,so that enough。 若是自己節點可以完成任務,則將結果輸出,終止傳遞。性能
用代碼標識即爲:this
function Me (flag){ if(flag===1){ console.log("I can do this homeword"); }else{ console.log("I can't :(, but u can do this ?"); XiaoFang(flag); } } function XiaoFang (flag){ if(flag===1){ console.log("I can do this homeword"); }else{ console.log("I can't :(, but u can do this ?"); XiaoJi(flag); } } function XiaoJi (flag){ if(flag===0){ console.log("I can do this homeword"); }else{ console.log("I can't :(, but u can do this ?"); //...繼續詢問下一我的 } } Me(0);
沒錯,職責鏈的主要內涵就是,若是你不行,在問問下一我的行不行。。。可是上面代碼讓我有種想kill people的衝動(不是寫的爛,是寫的太爛了),惟一可以表揚他的就是,知道職責鏈模式的原理。因此爲了情懷,咱們須要給上面的代碼換一身皮.prototype
function Chain(fn){ this.fn = fn; this.nextExer = null; } Chain.prototype.setNext = function(obj){ this.nextExer = obj; } Chain.prototype.exe = function(flag){ var result = this.fn.apply(this,arguments); if(result === "next"){ this.next(flag); } } Chain.prototype.next = function(){ return this.nextExer.exe.apply(this.nextExer,arguments) } var fn1 = new Chain(function(flag){ if(flag===1){ console.log("I can do this homework"); }else{ console.log("I can't do this homework"); return "next"; } }); var fn2 = new Chain(function(flag){ if(flag===1){ console.log("I can do this homework"); }else{ console.log("I can't do this homework"); return "next"; } }) var fn3 = new Chain(function(flag){ if(flag===0){ console.log("I can do this homework"); }else{ console.log("I can't do this homework"); return "next"; } }) fn1.setNext(fn2); fn2.setNext(fn3); fn1.exe(0);
雖然,上面這段代碼看起來清晰不少,使用next調用下一個函數,使用exe初始化.可是看起來在setNext哪裏有點囉嗦。咱們試着改進:設計
Chain.prototype.setNext = function(obj){ this.nextExer = obj; return obj; } fn1.setNext(fn2).setNext(fn3); fn1.exe(0);
只須要將setNext哪裏返回一個Obj,就能夠獲得完美的鏈式調用了。能夠從上面的代碼中看出一些端倪,在職責鏈模式中,咱們須要規定,在每一個exe執行事後須要設置一個result,而且這個result必須能明確的標識下一個到底繼不繼續。code
固然,要知道,這個職責鏈模式並非必定要把管理權交給內部執行,你固然也能夠在外面進行判斷和設置。接口
var fn2 = new Chain(function(flag){ console.log("I can't do this homework"); this.nextExer.fn(0); //手動執行下一個 })
經過上面的步驟,能夠在外部直接判斷,是否執行下一個。因此職責模式的寫法也是不少的。
並且,職責鏈最大的一個好處就是,你能夠從鏈中,任意一個節點開始遍歷。 咱們用上面那個例子體會一下。
假如,我前面座的童鞋,我和他都同時喜歡一女生,因此我倆關係超差。我固然不能問情敵要做業啦,這時候,我能夠再往前一個同窗問。利用職責模式就爲.
xiaoMing.setNext(xiaoFang).setNext(xiaoJi); //改寫,直接從小芳開始 xiaoFang.setNext(xiaoJi);
這應該算是職責鏈模式的一大特點,可是這個也不是沒有問題的,就是咱們須要在最後一個節點上加上判斷,表示若是沒有其餘處理程序,並且在該節點上也不成立的話,則須要拋出一個錯誤,或者作出相應的說明. 而且,咱們每次請求的時候,都會從節點鏈的開始進行遍歷,這樣極可能會形成性能的損失,因此這裏須要注意的是,不要設置太長的職責鏈。
這裏AOP指的是面向切面編程,即將其餘函數動態的加入到一個函數中,好比before & after. 咱們仔細想一想,一個隊列無外乎就是在前在後的關係,因此一個before和after已經算是萬能的了(排除你有動態刪除的需求)。
Function.prototype.after = function(fn){ var _this = this; return function(){ var res = _this.apply(this,arguments); if(!res){ //值爲Boolean return fn.apply(this,arguments); } return res; } } Function.prototype.before = function(fn){ var _this = this; return function(){ fn.apply(this,arguments); return _this.apply(this,arguments); } }
上面已經將AOP中兩個最重要的before和after添加到Function的原型裏面了。
如今咱們可使用這兩把三相之力開啓職責鏈模式
XiaoMing.after(XiaoFang).after(XiaoJi);
我操,完美啊,通俗易懂哎喂。
若是咱們須要加上判斷的話,能夠直接在after和before裏面寫
//只舉before的例子吧 Function.prototype.before = function(fn){ var _this = this; return function(){ var res = fn.apply(this,arguments); //值爲Boolean,表示是否繼續向下傳遞 if(res===false){ //若是返回不成立,則繼續向下傳遞 return _this.apply(this,arguments); } } } function Xiaoming(){ console.log("I can do this homework"); return "ok"; //中斷返回,固然這裏你能夠隨便定義,除了"next" } function XiaoFang(){ console.log("I can't do this homework"); return "next"; } Xiaoming. before(XiaoFang)();
咱們這裏再次回憶一下職責鏈模式的用處,將一個請求依照一條鏈傳遞,若是有個知足則斷開傳遞,返回結果。 想想,這個和咱們的迭代器模式有着殊途同歸的妙處,迭代器模式一樣也是遍歷選出最優解,可是相比而言,職責鏈模式的直觀性個書寫的幸福感是遠遠超過迭代器模式的。
在寫一些hacks的時候,不免會用到if...else if...判斷語句,上次咱們使用迭代器模式完成這樣的功能,可是效果不是很理想,這裏咱們使用職責鏈模式完成。
事件模式的選擇函數
Function.prototype.after = function(fn){ var _this = this; return function(){ var res = _this.apply(this,arguments); if(res==="next"){ //值爲Boolean return fn.apply(this,arguments); } return res; } } var bind = (function() { var DOM2 = function() { if (document.addEventListener) { return function(ele, fn, type) { ele.addEventListener(type, () => { fn(); }, false); } } else { return "next"; } }; var IE = function() { if (document.attachEvent) { return function(ele, fn, type) { ele.attachEvent(type, fn); } } else { return "next"; } }; var DOM0 = function(){ return function(ele, fn, type) { ele[`on${type}`] = () => { fn(); }; } } return DOM2.after(IE).after(DOM0)(); })(); console.log(bind);
恩,以上結果只是一個簡單地示範。 這裏須要提個醒,職責鏈模式是設計模式中最容易忘記的模式之一,由於它好用到不叫模式。因此,職責鏈模式的用法也是不少的,但願你們多多探索,將本身學到的只是分享出來,這是,極好的呀!