1. 程式人生 > 其它 >JavaScript設計模式 職責鏈模式

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 節點中的事件冒泡,我們都能從中找到職責鏈模式的影子。職責鏈模式還可以和組合模式結合在一起,用來連線部件和父部件,或是提高組合物件的效率。