iT邦幫忙

2019 iT 邦幫忙鐵人賽

DAY 12
0
Modern Web

JS Design Pattern 系列 第 12

JS Design Pattern Day12-職責鏈模式 Chain of Responsibility(上)

嗨大家好,居然就這麼過了12天真令人吃驚。這幾天剛好是我在抉擇人生重大事情的時候,忍不住一直想到一句話:決定我們成為怎樣的人的,不是我們的才能,而是我們的選擇。

今日,職責鏈模式(上),我們開始吧

職責鏈模式的定義是 讓多個物件都有機會處理請求,從而避免請求的發送者和接收者之間的耦合關係。

舉生活的例子來說,在考試作弊的時候你可以把不會的題目寫在小紙條上往後面的同學傳,會的人就會寫答案上去再回傳,這樣就是一種職責鏈模式,你不會一開始就知道誰會寫就直接傳給他,反而會一個一個的傳,這樣就把你跟回答者之間的耦合打斷了,
你也就不必一開始就先調查看誰會這題然後再傳給他了。

實際寫段code來體會一下,假設你是某電商,現在提供先訂購500元就送優惠券100元、訂購200元就送優惠券50元
的活動,若沒有提前訂購就必須在倉庫有貨的狀況下才能買到:

//在這裡先用orderType變數當作訂購金額的種類,1就是500、2是200,3是沒有訂購
//isPaid代表是否有訂購
//stock代表存貨數量
var order = function (oredereType, isPaid, stock) {
    if (oredereType === 1) {
        if (isPaid === true) {
            console.log('訂購500,得到一張100優惠卷');
        } else {
            if (stock > 0) {
                console.log('有貨,但無任何優貨');
            } else {
                console.log('沒貨');
            }
        }
    } else if (oredereType === 2) {
        if (isPaid === true) {
            console.log('訂購200,得到一張50優惠卷');
        } else {
            if (stock > 0) {
                console.log('有貨,但無任何優貨');
            } else {
                console.log('沒貨');
            }
        }
    } else {
        if (stock > 0) {
            console.log('有貨,但無任何優貨');
        } else {
            console.log('沒貨');
        }
    }
};

order(1, true, 10);

顯然這樣寫就違反一堆原則我都懶得打了,我們用職責鏈模式改造一下:
首先我們把各種訂購方式分成各自的函數

var order500 = function (oredereType, isPaid, stock) {
    if (oredereType === 1 && isPaid === true) {
        console.log('訂購500,得到一張100優惠卷');
    } else {
        order200(oredereType, isPaid, stock);
    }
};
var order200 = function (oredereType, isPaid, stock) {
    if (oredereType === 2 && isPaid === true) {
        console.log('訂購200,得到一張50優惠卷');
    } else {
        orderNormal(oredereType, isPaid, stock);
    }
};
var orderNormal = function (oredereType, isPaid, stock) {
    if (stock > 0) {
        console.log('有貨,但無任何優貨');
    } else {
        console.log('沒貨');
    }
};
order500(1, true, 10);

基本上執行結果跟上一段寫法是一樣的,雖然我們把大函數拆成3個小函數,但在傳遞過程仍然非常僵硬,如果今天要加入300元優惠的方案勢必就要拆掉或是修改原本的函數,我們再用個更靈活的寫法:
在原本的函數中會直接指定下一個節點,現在我們直接限定,若不符合自己函數的直接回應一個自串‘nextStage’表示要進入下一個節點

var order500 = function (oredereType, isPaid, stock) {
    if (oredereType === 1 && isPaid === true) {
        console.log('訂購500,得到一張100優惠卷');
    } else {
        return 'nextStage';
    }
};
var order200 = function (oredereType, isPaid, stock) {
    if (oredereType === 2 && isPaid === true) {
        console.log('訂購200,得到一張50優惠卷');
    } else {
        return 'nextStage';
    }
};
var orderNormal = function (oredereType, isPaid, stock) {
    if (stock > 0) {
        console.log('有貨,但無任何優貨');
    } else {
        console.log('沒貨');
    }
};

接下來我們要做一個管理整個職務鏈的物件

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

//指定下一個節點
Chain.prototype.setNextStage = function (stage) {
    return this.stage = stage;
};

//傳遞請求給某個節點
Chain.prototype.passRequest = function () {
    var ret = this.fn.apply(this, arguments);
    if (ret === 'nextStage') {
        return this.stage && this.stage.passRequest.apply(this.stage, arguments);
    }
    return ret;
};

接下來把每個方法都丟入Chain建構函數來建立一個節點

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

接下來設定每個節點的順序

chainOrder500.setNextStage(chainOrder200);
chainOrder200.setNextStage(chainOrderNormal);

把請求丟給整個鏈的第一個節點試試看

chainOrder500.passRequest(1, true, 10);

結果就跟原本的一樣,如果我們現在要加入一個新方法

var order300 = function () {//省略實作
};
var chainOrder300 = new Chain(order300);
chainOrder500.setNextStage(order300);
chainOrder300.setNextStage(order200);

整個職責鏈改下去之後工程師改起需求就簡單許多,過程中也不需要去了解前後節點實作內容,要做的只有增加一個節點,並設置前後節點即可。


上一篇
JS Design Pattern Day11-輕量模式 Flyweight(下)
下一篇
JS Design Pattern Day13-職責鏈模式 Chain of Responsibility(下)
系列文
JS Design Pattern 30

尚未有邦友留言

立即登入留言