cover

你去辦護照的經驗是什麼?先到櫃台 A 填表 → 櫃台 B 驗身份 → 櫃台 C 繳費 → 櫃台 D 拍照。每個櫃台只管自己的事,做完就把你傳給下一個。

Chain of Responsibility 就是這種「關卡式」的處理流程。

先講結論

把請求沿著一條「處理鏈」傳遞,每個 handler 決定自己要不要處理、處理完要不要繼續往下傳。你可以動態增減 handler,不用改任何既有的程式碼。

classDiagram
    class Handler {
        <<abstract>>
        -nextHandler : Handler
        +setNext(handler) Handler
        +handle(request)* any
    }
    class StockHandler {
        +handle(request) any
    }
    class DiscountHandler {
        +handle(request) any
    }
    class PaymentHandler {
        +handle(request) any
    }
    Handler <|-- StockHandler
    Handler <|-- DiscountHandler
    Handler <|-- PaymentHandler
    Handler --> Handler : nextHandler

實戰:購物車結帳流程

庫存不夠?直接擋掉。庫存 OK?看看有沒有折扣。最後才處理付款。

Chain of Responsibility 模式:請求依序通過檢查關卡

class Handler {
    constructor() { this.nextHandler = null; }
 
    setNext(handler) {
        this.nextHandler = handler;
        return handler; // 回傳 handler 方便 chain 起來
    }
 
    handle(request) {
        if (this.nextHandler) return this.nextHandler.handle(request);
        return null;
    }
}
 
class StockHandler extends Handler {
    handle(request) {
        if (request.quantity > 100) {
            return { success: false, message: '庫存不足' };
        }
        console.log('庫存檢查通過');
        return super.handle(request);
    }
}
 
class DiscountHandler extends Handler {
    handle(request) {
        if (request.amount > 1000) {
            request.discount = 0.1;
            console.log('套用 10% 折扣');
        }
        return super.handle(request);
    }
}
 
class PaymentHandler extends Handler {
    handle(request) {
        if (!request.paymentMethod) {
            return { success: false, message: '缺少付款方式' };
        }
        console.log('付款處理完成');
        return { success: true, message: '訂單完成' };
    }
}
 
// 串起來
const stock = new StockHandler();
const discount = new DiscountHandler();
const payment = new PaymentHandler();
 
stock.setNext(discount).setNext(payment);
 
const result = stock.handle({
    quantity: 5,
    amount: 1500,
    paymentMethod: 'credit_card'
});
// 庫存檢查通過 → 套用 10% 折扣 → 付款處理完成

想在庫存檢查後面加一個「黑名單檢查」?寫一個 BlacklistHandler,插進鏈裡就好。其他 handler 完全不用改。

你已經在用了:Express Middleware

Express 的 app.use() 就是 Chain of Responsibility。每個 middleware 做完自己的事,呼叫 next() 傳給下一個。沒呼叫 next() 就停止傳遞。

小心的地方

  • 請求可能沒人處理:鏈走完了但沒有任何 handler 處理,要有 fallback
  • 鏈太長難 debug:請求經過十個 handler,出問題你要一個一個追

Chain of Responsibility 就像公司的簽核流程——一層一層往上簽,每個人只看自己該看的。問題是鏈條太長的時候,你的請假單要簽到下個月


延伸閱讀