職責鏈模式的定義是:使多個對象都有機會處理請求,從而避免請求的發送者和接收者之間的耦合關係,將這些對象連成一條鏈,並沿着這條鏈傳遞該請求,直到有一個對象處理它爲止。
假設我們負責一個售賣手機的電商網站,經過分別交納 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);