JavaScript設計模式 職責鏈模式
技術標籤:JavaScript設計模式js設計模式
一.生活場景
早高峰時,公交車上人實在太多了,經常上車後找不到售票員在哪,所以只好把兩塊錢硬幣往前面遞。除非你運氣夠好,站在你前面的第一個人就是售票員,否則,你的硬幣通常要在 N 個人手上傳遞,才能最終到達售票員的手裡。這種通過傳遞,最終傳到售票員的模式就是職責鏈模式。
二.定義
職責鏈模式(Chain of Responsibility Pattern):使多個物件都有機會處理請求,從而避免請求的傳送者和接收者之間的耦合關係,將這些物件連成一條鏈,並沿著這條鏈傳遞該請求,直到有一個物件處理它為止。
請求傳送者只需要知道鏈中的第一個節點,從而弱化了傳送者和一組接收者之間的強聯絡。
三.實現預售優惠功能
現在我們做一個預售付定金,正式購買時返優惠券的功能。定金500對應100優惠券,200對應50優惠券,未付定金原價購買。
實現流程如下:
/***** orderType:預付型別 pay:是否付款 stock:商品庫存 *****/
var order = function (orderType, pay, stock) {
if (orderType === 1) { // 500 元定金購買模式
if (pay === true) { // 已支付定金
console.log('500 元定金預購, 得到 100 優惠券');
} else { // 未支付定金,降級到普通購買模式
if (stock > 0) { // 用於普通購買的手機還有庫存
console.log('普通購買, 無優惠券');
} else {
console.log('手機庫存不足');
}
}
}
else if (orderType === 2) { // 200 元定金購買模式
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('手機庫存不足');
}
}
};
order(1, true, 500); // 輸出: 500 元定金預購, 得到 100 優惠券
雖然實現了功能,但是太多的if判斷失去了程式碼的閱讀性,如果在增加定金型別會不斷堆砌判斷,我們也需要增加可維護性,利用職責鏈的思想給它解耦。
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.prototype.setNextSuccessor 指定在鏈中的下一個節點
// Chain.prototype.passRequest 傳遞請求給某個節點
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;
};
//包裝成職責鏈的節點
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); // 輸出:手機庫存不足
如此我們解耦了不同預付方式的程式碼,如果再加入一個預付300返80優惠券型別,我們只需新增一個函式,放入職責鏈中執行。
var order300 = function (orderType, pay, stock) {
if (orderType === 1 && pay === true) {
console.log('300 元定金預購,得到 80 優惠券');
} else {
return 'nextSuccessor'; // 我不知道下一個節點是誰,反正把請求往後面傳遞
}
};
chainOrder300= new Chain( order300 );
chainOrder500.setNextSuccessor( chainOrder300);
chainOrder300.setNextSuccessor( chainOrder200);
四.非同步的職責鏈
上面的例子中我們模擬的是同步事件,開發中我們也會遇到一些非同步的問題,比如ajax請求等等。我們模擬一個職責鏈非同步實現。
var fn1 = new Chain(function () {
console.log(1);
return 'nextSuccessor';
});
var fn2 = new Chain(function () {
console.log(2);
var self = this;
setTimeout(function () {
self.next();
}, 1000);
});
var fn3 = new Chain(function () {
console.log(3);
});
fn1.setNextSuccessor(fn2).setNextSuccessor(fn3);
fn1.passRequest();
現在我們得到了一個特殊的鏈條,請求在鏈中的節點裡傳遞,但節點有權利決定什麼時候把請求交給下一個節點。
五.總結
職責鏈模式可以很好地幫助我們管理程式碼,降低發起請求的物件和處理請求的物件之間的耦合性。職責鏈中的節點數量和順序是可以自由變化的,我們可以在執行時決定鏈中包含哪些節點。無論是作用域鏈、原型鏈,還是 DOM 節點中的事件冒泡,我們都能從中找到職責鏈模式的影子。職責鏈模式還可以和組合模式結合在一起,用來連線部件和父部件,或是提高組合物件的效率。