Chain of Responsibility Pattern(責任鏈模式)
是什麼?
Chain of Responsibility Pattern 把多個處理者(Handler)串成一條鏈。請求沿著鏈傳遞,每個處理者要嘛處理請求,要嘛把請求傳給下一個處理者。發送者不需要知道誰會處理請求。
ℹ️GoF 分類
Chain of Responsibility 屬於行為型模式(Behavioral Pattern),重點在於解耦請求的發送者和處理者。
什麼時候用?
- 多個物件都可能處理請求,但具體由誰處理在執行時才確定
- 你想讓請求沿著一條鏈逐級傳遞
- 處理者可以動態增減或重新排序
- 你需要 Middleware Pipeline 架構
什麼時候不該用?
⚠️過度設計警告
如果處理者固定且數量少,直接用 if-else 判斷就夠了。責任鏈的缺點是請求可能沒有任何處理者處理(走到鏈尾都沒人接),且偵錯時需要追蹤整條鏈才能找到是誰處理的。
執行流程
定義 Handler 介面
宣告 Handle 方法和設定下一個 Handler 的方法
實作具體 Handler
每個 Handler 判斷自己能否處理請求
串連 Handler 鏈
把各 Handler 依序連接起來
Client 發送請求
把請求交給鏈的第一個 Handler
逐級傳遞
能處理就處理,不能處理就傳給下一個
流程解讀:CoR 的精髓是「解耦發送者和處理者」。Client 不需要知道哪個 Handler 會處理請求,只要把請求丟給鏈頭就好。每個 Handler 有自主權決定「我能不能處理」— 能處理就處理並回傳結果,不能處理就呼叫 base.Handle() 把請求傳給下一個。鏈的順序和組合可以在執行時動態調整。
程式碼範例
C# 版本
// 1. Handler 抽象類別
public abstract class ApprovalHandler
{
private ApprovalHandler? _next;
public ApprovalHandler SetNext(ApprovalHandler next)
{
_next = next;
return next; // 支援鏈式呼叫
}
public virtual string Handle(int leaveDays)
{
if (_next != null)
return _next.Handle(leaveDays);
return "沒有人有權限批准這個請假天數";
}
}
// 2. 具體 Handler
public class TeamLead : ApprovalHandler
{
public override string Handle(int leaveDays)
{
if (leaveDays <= 1)
return $"組長批准了 {leaveDays} 天假";
Console.WriteLine($" 組長:{leaveDays} 天超過我的權限,往上送");
return base.Handle(leaveDays);
}
}
public class Manager : ApprovalHandler
{
public override string Handle(int leaveDays)
{
if (leaveDays <= 3)
return $"經理批准了 {leaveDays} 天假";
Console.WriteLine($" 經理:{leaveDays} 天超過我的權限,往上送");
return base.Handle(leaveDays);
}
}
public class Director : ApprovalHandler
{
public override string Handle(int leaveDays)
{
if (leaveDays <= 7)
return $"總監批准了 {leaveDays} 天假";
Console.WriteLine($" 總監:{leaveDays} 天超過我的權限,往上送");
return base.Handle(leaveDays);
}
}
// 3. 使用
var teamLead = new TeamLead();
var manager = new Manager();
var director = new Director();
teamLead.SetNext(manager).SetNext(director);
Console.WriteLine(teamLead.Handle(1)); // 組長批准了 1 天假
Console.WriteLine(teamLead.Handle(3)); // 經理批准了 3 天假
Console.WriteLine(teamLead.Handle(5)); // 總監批准了 5 天假
Console.WriteLine(teamLead.Handle(10)); // 沒有人有權限批准TypeScript 版本
// 1. Handler 抽象類別
abstract class ApprovalHandler {
private next?: ApprovalHandler;
setNext(next: ApprovalHandler): ApprovalHandler {
this.next = next;
return next;
}
handle(leaveDays: number): string {
if (this.next) return this.next.handle(leaveDays);
return "沒有人有權限批准這個請假天數";
}
}
// 2. 具體 Handler
class TeamLead extends ApprovalHandler {
handle(leaveDays: number): string {
if (leaveDays <= 1) return `組長批准了 ${leaveDays} 天假`;
console.log(` 組長:${leaveDays} 天超過我的權限,往上送`);
return super.handle(leaveDays);
}
}
class Manager extends ApprovalHandler {
handle(leaveDays: number): string {
if (leaveDays <= 3) return `經理批准了 ${leaveDays} 天假`;
console.log(` 經理:${leaveDays} 天超過我的權限,往上送`);
return super.handle(leaveDays);
}
}
class Director extends ApprovalHandler {
handle(leaveDays: number): string {
if (leaveDays <= 7) return `總監批准了 ${leaveDays} 天假`;
console.log(` 總監:${leaveDays} 天超過我的權限,往上送`);
return super.handle(leaveDays);
}
}
// 3. 使用
const teamLead = new TeamLead();
const manager = new Manager();
const director = new Director();
teamLead.setNext(manager).setNext(director);
console.log(teamLead.handle(1)); // 組長批准了 1 天假
console.log(teamLead.handle(3)); // 經理批准了 3 天假
console.log(teamLead.handle(5)); // 總監批准了 5 天假
console.log(teamLead.handle(10)); // 沒有人有權限批准Python 版本
from abc import ABC
# 1. Handler 抽象類別
class ApprovalHandler(ABC):
def __init__(self):
self._next: ApprovalHandler | None = None
def set_next(self, next_handler: "ApprovalHandler") -> "ApprovalHandler":
self._next = next_handler
return next_handler
def handle(self, leave_days: int) -> str:
if self._next:
return self._next.handle(leave_days)
return "沒有人有權限批准這個請假天數"
# 2. 具體 Handler
class TeamLead(ApprovalHandler):
def handle(self, leave_days: int) -> str:
if leave_days <= 1:
return f"組長批准了 {leave_days} 天假"
print(f" 組長:{leave_days} 天超過我的權限,往上送")
return super().handle(leave_days)
class Manager(ApprovalHandler):
def handle(self, leave_days: int) -> str:
if leave_days <= 3:
return f"經理批准了 {leave_days} 天假"
print(f" 經理:{leave_days} 天超過我的權限,往上送")
return super().handle(leave_days)
class Director(ApprovalHandler):
def handle(self, leave_days: int) -> str:
if leave_days <= 7:
return f"總監批准了 {leave_days} 天假"
print(f" 總監:{leave_days} 天超過我的權限,往上送")
return super().handle(leave_days)
# 3. 使用
team_lead = TeamLead()
manager = Manager()
director = Director()
team_lead.set_next(manager).set_next(director)
print(team_lead.handle(1)) # 組長批准了 1 天假
print(team_lead.handle(3)) # 經理批准了 3 天假
print(team_lead.handle(5)) # 總監批准了 5 天假
print(team_lead.handle(10)) # 沒有人有權限批准Java 版本
// 1. Handler 抽象類別
public abstract class ApprovalHandler {
private ApprovalHandler next;
public ApprovalHandler setNext(ApprovalHandler next) {
this.next = next;
return next;
}
public String handle(int leaveDays) {
if (next != null) return next.handle(leaveDays);
return "沒有人有權限批准這個請假天數";
}
}
// 2. 具體 Handler
public class TeamLead extends ApprovalHandler {
public String handle(int leaveDays) {
if (leaveDays <= 1) return "組長批准了 " + leaveDays + " 天假";
System.out.println(" 組長:" + leaveDays + " 天超過我的權限,往上送");
return super.handle(leaveDays);
}
}
public class Manager extends ApprovalHandler {
public String handle(int leaveDays) {
if (leaveDays <= 3) return "經理批准了 " + leaveDays + " 天假";
System.out.println(" 經理:" + leaveDays + " 天超過我的權限,往上送");
return super.handle(leaveDays);
}
}
public class Director extends ApprovalHandler {
public String handle(int leaveDays) {
if (leaveDays <= 7) return "總監批准了 " + leaveDays + " 天假";
System.out.println(" 總監:" + leaveDays + " 天超過我的權限,往上送");
return super.handle(leaveDays);
}
}
// 3. 使用
ApprovalHandler teamLead = new TeamLead();
ApprovalHandler manager = new Manager();
ApprovalHandler director = new Director();
teamLead.setNext(manager).setNext(director);
System.out.println(teamLead.handle(1)); // 組長批准了 1 天假
System.out.println(teamLead.handle(3)); // 經理批准了 3 天假
System.out.println(teamLead.handle(5)); // 總監批准了 5 天假
System.out.println(teamLead.handle(10)); // 沒有人有權限批准結構圖
結構解讀:Client 只跟鏈的第一個 Handler(TeamLead)互動,不知道後面還有誰。每個 Handler 透過 next 指標連接到下一個,形成單向鏈。請求沿著 A → B → C 傳遞,直到某個 Handler 處理或鏈尾返回預設結果。Handler 的順序和組合可以在執行時動態調整,這是比 if-else 更靈活的地方。
實戰補充
💡資深開發者經驗
Middleware Pipeline:ASP.NET Core 的 Middleware、Express.js 的 app.use()、Django 的 Middleware 都是責任鏈的變體。每個 Middleware 可以處理請求、修改請求、或把請求傳給下一個。
Exception Handler Chain:多層 try-catch 其實就是責任鏈。內層 catch 不到的例外會往外層拋,直到有人處理或程式崩潰。
Log Filter:日誌系統常用責任鏈——DEBUG → INFO → WARN → ERROR。每一層只處理自己等級的日誌,其餘往上傳。
兩種變體:GoF 的原版是「只有一個處理者會處理」,但實際應用中常見「每個處理者都處理一部分然後往下傳」(Pipeline 風格),例如 Middleware 對請求做驗證、記錄、壓縮等。
理解測驗
🤔 Chain of Responsibility 和直接用 if-else 相比,最大的優勢是什麼?
🤔 如果請求沿著責任鏈走到最後都沒有人處理,會發生什麼?
🤔 以下哪個是 Chain of Responsibility 在現代框架中的實際應用?
面試常見問題
Q: GoF 原版的 CoR 和 Middleware Pipeline 有什麼差別?
A: GoF 原版是「只有一個 Handler 處理」— 請求找到處理者就停下。Middleware Pipeline 是「每個 Handler 都處理一部分然後往下傳」— 每一層可以在 next() 前後各做一些事(如 ASP.NET Core Middleware 的 Request/Response 管道)。Pipeline 風格更像 Decorator Chain,但語意上歸類為 CoR 的變體。
Q: 如果請求走到鏈尾都沒人處理,該怎麼設計?
A: 三種常見策略:(1) Default Handler — 鏈尾放一個兜底處理者。(2) 拋出例外 — base class 的 Handle() 在 next 為 null 時拋出 UnhandledRequestException。(3) 靜默忽略 — 返回預設值(如範例的「沒有人有權限批准」)。選擇取決於業務需求:關鍵操作用例外,非關鍵操作用預設值。
相關模式
| 模式 | 關係 | |------|------| | Decorator | 結構相似(都是鏈式結構),但 Decorator 每層都執行,CoR 只有一個處理 | | Composite | CoR 的 Handler 可以是 Composite 結構的一部分 — 子節點處理不了就往父節點傳 | | Command | Command 封裝請求,CoR 決定誰來處理這個請求。兩者常搭配使用 | | Mediator | 都能處理請求分發,但 CoR 是分散式(一條鏈),Mediator 是集中式(一個中心) |
重點整理
💡一句話記住
Chain of Responsibility = 審批鏈:逐級往上送,能批就批。 口訣:「鏈式傳遞找處理者,動態增減不改碼」
| 概念 | 說明 | |------|------| | Handler(處理者介面) | 定義 Handle 方法和鏈接下一個 Handler 的方法 | | ConcreteHandler | 判斷自己能否處理,不能就傳給下一個 | | Client | 把請求交給鏈的第一個 Handler | | 核心好處 | 解耦發送者和處理者,處理者可動態增減 | | 代價 | 請求可能沒人處理,偵錯需追蹤整條鏈 |