傳遞請求之職責鏈模式

職責鏈模式其實很好理解,因爲一個鏈字出賣了它的靈魂。咱們能夠從這個字獲得很大的提示。首先這個模式必定有傳遞性,並且,節點是能夠重複拼接的,而且每一個節點都具備必定的過濾功能,必定的職責。編程

是否是想起了組合模式裏的一些內容呢? 是的,他們兩個有着自然的相似點,不過組合模式主要職責是給執行者添加一些列行爲,而不區份內部的執行。職責鏈模式則會強調內部的細節,他能夠手動傳遞權限,手動終止權限。設計模式

舉個栗子吧: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

這裏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);

恩,以上結果只是一個簡單地示範。 這裏須要提個醒,職責鏈模式是設計模式中最容易忘記的模式之一,由於它好用到不叫模式。因此,職責鏈模式的用法也是不少的,但願你們多多探索,將本身學到的只是分享出來,這是,極好的呀!

相關文章
相關標籤/搜索