職責鏈模式

職責鏈模式的定義是:使多個對象都有機會處理請求,從而避免請求的發送者和接收者之間的耦合關係,將這些對象連成一條鏈,並沿着這條鏈傳遞該請求,直到有一個對象處理它爲止。

假設我們負責一個售賣手機的電商網站,經過分別交納 500 元定金和 200 元定金的兩輪預定 後(訂單已在此時生成),現在已經到了正式購買的階段。

公司針對支付過定金的用戶有一定的優惠政策。在正式購買後,已經支付過 500 元定金的用 戶會收到 100 元的商城優惠券,200 元定金的用戶可以收到 50 元的優惠券,而之前沒有支付定金的用戶只能進入普通購買模式,也就是沒有優惠券,且在庫存有限的情況下不一定保證能買到。

將這個流程變成代碼如下所示:

// orderType 表示訂單類型
// 值爲1時表示500元定金用戶,值爲2時表示200元定金用戶,值爲3時表示普通用戶
// pay表示用戶是否支付定金,true表示已經支付,false表示沒有支付
// stock表示當前用於普通購買的手機庫存數量,已經支付過500元或者200元定金的用戶不受此限制
var order = function(orderType, pay, stock) {
    if (orderType === 1) {
        if (pay === true) {
            console.log('500元定金預購,得到100優惠券');
        } else {
            if (stock > 0) {
                console.log('普通購買,無優惠券');
            } else {
                console.log('手機庫存不足');
            }
        }
    } else if (orderType === 2) {
        if (pay === true) {
            console.log('200元定金預購,得到50優惠券');
        } else {
            if (stock > 0) {
                console.log('普通購買,無優惠券');
            } else {
                console.log('手機庫存不足');
            }
        }
    } else if (orderType === 3) {
        if (stock > 0) {
            console.log('普通購買,無優惠券');
        } else {
            console.log('手機庫存不足');
        }
    }
}

但是這段代碼過於巨大,難以修改,不利於維護。使用職責鏈可以將這個函數拆分成500元定金函數,200元定金函數,普通用戶函數,接下來把 orderType、pay、stock 這 3 個字段當作參數傳遞給 500 元訂單函數,如果該函數不 符合處理條件,則把這個請求傳遞給後面的 200 元訂單函數,如果 200 元訂單函數依然不能處理 該請求,則繼續傳遞請求給普通購買函數,代碼如下:

var order500 = function( orderType, pay, stock ) {
    if (orderType === 1 && pay === true){ 
        console.log( '500 元定金預購,得到 100 優惠券' ); 
    } else { 
        return 'nextSuccessor'; // 我不知道下一個節點是誰,反正把請求往後面傳遞
    } 
}; 

var order200 = function( orderType, pay, stock ) {
    if (orderType === 2 && pay === true) { 
        console.log( '200 元定金預購,得到 50 優惠券' ); 
    } else { 
        return 'nextSuccessor'; // 我不知道下一個節點是誰,反正把請求往後面傳遞 
    } 
}; 

var orderNormal = function( orderType, pay, stock ) { 
    if (stock > 0) {
        console.log( '普通購買,無優惠券' ); 
    } else { 
        console.log( '手機庫存不足' ); 
    } 
};

接下來需要把函數包裝進職責鏈節點,我們定義一個構造函數 Chain,在 new Chain 的時候傳 遞的參數即爲需要被包裝的函數,同時它還擁有一個實例屬性 this.successor,表示在鏈中的下 一個節點。

// Chain.prototype.setNextSuccessor 指定在鏈中的下一個節點
// Chain.prototype.passRequset 傳遞請求給某個節點

var Chain = function(fn) {
    this.fn = fn;
    this.successor = null;
};

Chain.prototype.setNextSuccessor = function(successor) {
    return this.successor = successor;
};

Chain.prototype.passRequest = function() {
    var ret = this.fn.apply(this, arguments);

    if (ret === 'nextSuccessor') {
        return this.successor && this.successor.passRequest.apply(this.successor,                           
               arguments);
    }

    return ret;
};

現在我們把 3 個訂單函數分別包裝成職責鏈的節點:

var chainOrder500 = new Chain( order500 ); 
var chainOrder200 = new Chain( order200 ); 
var chainOrderNormal = new Chain( orderNormal ); 

然後指定節點在職責鏈中的順序:

chainOrder500.setNextSuccessor( chainOrder200 ); 
chainOrder200.setNextSuccessor( chainOrderNormal ); 

最後把請求傳遞給第一個節點:

chainOrder500.passRequest( 1, true, 500 ); // 輸出:500 元定金預購,得到 100 優惠券 
chainOrder500.passRequest( 2, true, 500 ); // 輸出:200 元定金預購,得到 50 優惠券 
chainOrder500.passRequest( 3, true, 500 ); // 輸出:普通購買,無優惠券 
chainOrder500.passRequest( 1, false, 0 ); // 輸出:手機庫存不足

我們可以利用 JavaScript 的函數式特性,有一種更加方便的方法來創建職責鏈。

Function.prototype.after = function (fn) {
    var self = this;

    return function() {
        var ret = self.apply(this, arguments);
        
        if (ret === 'nextSuccessor') {
            return fn.apply(this, argument);
        }

        return ret;
    }
};

var order = order500.after(order200).after(orderNormal);
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章