行為型設計模式 關注物件間的**責任分配**與**演算法封裝**,核心在於「物件如何互動」而非「物件結構如何組成」。GoF 定義了 11 種行為型模式,本文提供完整分類概覽,並深度解析最實用的 **責任鏈模式**。
| 模式 | 核心意圖 | 關鍵詞 | 常見應用 |
|---|---|---|---|
| 責任鏈 | 讓多個物件有機會處理請求,避免發送者與接收者耦合 | 鏈式、遞交、動態組合 | 權限驗證、日誌處理、異常處理、中介軟體管線 |
| 命令 | 將請求封裝為物件,支援撤銷、佇列、記錄 | 封裝、參數化、可撤銷 | GUI 按鈕、巨集錄製、交易系統、工作佇列 |
| 解譯器 | 定義語言文法並解譯執行 | 文法、AST、解譯器 | 規則引擎、查詢語言、腳本引擎、正則運算式 |
| 迭代器 | 統一存取聚合物件元素,不暴露內部結構 | 遍歷、封裝、多態 | IEnumerable、集合遍歷、樹狀結構走訪 |
| 中介者 | 集中管理物件間互動,降低耦合 | 集中、解耦、協調 | 表單驗證、聊天室、航空管制、MVC Controller |
| 備忘錄 | 捕獲物件內部狀態以便稍後恢復 | 快照、狀態、撤銷 | 編輯器撤銷、遊戲存檔、資料庫交易回滾 |
| 觀察者 | 定義一對多依賴,狀態變更自動通知 | 發布/訂閱、事件、反應式 | 事件系統、資料繫結、訊息佇列、React/Vue 響應式 |
| 狀態 | 允許物件在內部狀態改變時改變行為 | 狀態機、行為變化、封裝 | 訂單流程、遊戲角色狀態、TCP 連線狀態、工作流 |
| 策略 | 定義演算法族群,封裝並可互換 | 演算法、可換、參數化 | 排序策略、支付方式、壓縮演算法、路由演算法 |
| 模板方法 | 定義演算法骨架,延遲步驟至子類別 | 骨架、鉤子、好萊塢原則 | 資料存取模板、報表產生、演算法框架、單元測試基類 |
| 訪問者 | 在不修改類別前提下新增操作 | 雙重分派、操作分離 | 編譯器 AST 走訪、報表匯出、物件序列化 |
避免「巨大的 if-else / switch 鏈」處理請求,將處理邏輯分散到獨立的處理器物件,形成鏈結。請求沿鏈傳遞,直到某處理器處理或鏈結結束。
// 抽象處理者
public abstract class Handler
{
protected Handler _successor;
public void SetSuccessor(Handler successor)
{
_successor = successor;
}
public abstract void HandleRequest(int request);
}
// 具體處理者 1:處理 0-9
public class ConcreteHandler1 : Handler
{
public override void HandleRequest(int request)
{
if (request >= 0 && request < 10)
{
Console.WriteLine($"Handler1 處理請求: {request}");
}
else if (_successor != null)
{
_successor.HandleRequest(request);
}
}
}
// 具體處理者 2:處理 10-19
public class ConcreteHandler2 : Handler
{
public override void HandleRequest(int request)
{
if (request >= 10 && request < 20)
{
Console.WriteLine($"Handler2 處理請求: {request}");
}
else if (_successor != null)
{
_successor.HandleRequest(request);
}
}
}
// 具體處理者 3:處理 20-29
public class ConcreteHandler3 : Handler
{
public override void HandleRequest(int request)
{
if (request >= 20 && request < 30)
{
Console.WriteLine($"Handler3 處理請求: {request}");
}
else if (_successor != null)
{
_successor.HandleRequest(request);
}
else
{
Console.WriteLine($"請求 {request} 無人處理 (超出範圍)");
}
}
}
// 客戶端建立鏈結
var h1 = new ConcreteHandler1();
var h2 = new ConcreteHandler2();
var h3 = new ConcreteHandler3();
h1.SetSuccessor(h2);
h2.SetSuccessor(h3);
// 發送請求
int[] requests = { 2, 5, 14, 22, 18, 3, 27, 20, 35 };
foreach (int req in requests)
{
h1.HandleRequest(req);
}
public interface IRequestHandler
{
void Handle(Request request);
IRequestHandler SetNext(IRequestHandler next);
}
public abstract class BaseHandler : IRequestHandler
{
private IRequestHandler _next;
public IRequestHandler SetNext(IRequestHandler next)
{
_next = next;
return next; // 支援鏈式呼叫: h1.SetNext(h2).SetNext(h3)
}
public virtual void Handle(Request request)
{
if (_next != null)
_next.Handle(request);
else
OnUnhandled(request);
}
protected virtual void OnUnhandled(Request request)
{
Console.WriteLine($"[未處理] {request}");
}
}
// 請求物件
public record Request(int Code, string Data, DateTime Timestamp = default)
{
public Request(int code, string data) : this(code, data, DateTime.Now) { }
}
// 具體處理器
public class AuthenticationHandler : BaseHandler
{
public override void Handle(Request request)
{
if (request.Code == 1001) // 假設 1001 為驗證請求
{
Console.WriteLine($"[驗證] 處理登入請求: {request.Data}");
// 驗證邏輯...
}
else
{
base.Handle(request); // 傳遞給下一個
}
}
}
public class AuthorizationHandler : BaseHandler
{
public override void Handle(Request request)
{
if (request.Code == 1002)
{
Console.WriteLine($"[授權] 檢查權限: {request.Data}");
}
else
{
base.Handle(request);
}
}
}
public class LoggingHandler : BaseHandler
{
public override void Handle(Request request)
{
Console.WriteLine($"[日誌] 記錄請求: Code={request.Code}, Data={request.Data}");
base.Handle(request); // 日誌通常都要記錄,再傳遞
}
}
public class DefaultHandler : BaseHandler
{
public override void Handle(Request request)
{
Console.WriteLine($"[預設] 兜底處理: {request.Code}");
// 不再傳遞,鏈結結束
}
}
// 建立鏈結 (可透過 DI 容器自動組裝)
var chain = new LoggingHandler();
chain.SetNext(new AuthenticationHandler())
.SetNext(new AuthorizationHandler())
.SetNext(new DefaultHandler());
// 發送請求
chain.Handle(new Request(1001, "user=admin"));
chain.Handle(new Request(1002, "resource=order"));
chain.Handle(new Request(9999, "unknown"));
// ASP.NET Core 中介軟體本質就是責任鏈
public class CustomMiddleware
{
private readonly RequestDelegate _next;
public CustomMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task InvokeAsync(HttpContext context)
{
// 前置處理 (請求進入)
Console.WriteLine($"[Middleware] Request: {context.Request.Path}");
// 呼叫下一個中介軟體 (傳遞給鏈結下一環)
await _next(context);
// 後置處理 (回應離開)
Console.WriteLine($"[Middleware] Response: {context.Response.StatusCode}");
}
}
// Startup.cs 組裝鏈結
app.UseMiddleware();
app.UseAuthentication(); // 驗證中介軟體
app.UseAuthorization(); // 授權中介軟體
app.UseMiddleware(); // 錯誤處理 (通常在最後)
app.MapControllers(); // 終端處理器
// 請求物件
public record PurchaseRequest(int Number, decimal Amount, string Purpose);
// 抽象審核者
public abstract class Approver
{
protected Approver _successor;
protected string _name;
protected Approver(string name) => _name = name;
public void SetSuccessor(Approver successor) => _successor = successor;
public abstract void ProcessRequest(PurchaseRequest request);
}
// 具體審核層級
public class Manager : Approver
{
public Manager(string name) : base(name) { }
public override void ProcessRequest(PurchaseRequest request)
{
if (request.Amount < 10000)
{
Console.WriteLine($"[經理 {_name}] 核准採購單 #{request.Number} (${request.Amount:N0})");
}
else if (_successor != null)
{
Console.WriteLine($"[經理 {_name}] 金額超過權限,轉呈上級...");
_successor.ProcessRequest(request);
}
}
}
public class Director : Approver
{
public Director(string name) : base(name) { }
public override void ProcessRequest(PurchaseRequest request)
{
if (request.Amount < 50000)
{
Console.WriteLine($"[總監 {_name}] 核准採購單 #{request.Number} (${request.Amount:N0})");
}
else if (_successor != null)
{
Console.WriteLine($"[總監 {_name}] 金額超過權限,轉呈副總...");
_successor.ProcessRequest(request);
}
}
}
public class VicePresident : Approver
{
public VicePresident(string name) : base(name) { }
public override void ProcessRequest(PurchaseRequest request)
{
if (request.Amount < 100000)
{
Console.WriteLine($"[副總 {_name}] 核准採購單 #{request.Number} (${request.Amount:N0})");
}
else
{
Console.WriteLine($"[副總 {_name}] 採購單 #{request.Number} (${request.Amount:N0}) 需提報董事會!");
}
}
}
// 使用
var manager = new Manager("王大明");
var director = new Director("李中正");
var vp = new VicePresident("張建國");
manager.SetSuccessor(director);
director.SetSuccessor(vp);
var requests = new[]
{
new PurchaseRequest(2034, 3500, "辦公用品"),
new PurchaseRequest(2035, 32590, "專案 X 設備"),
new PurchaseRequest(2036, 122100, "專案 Y 機房建置")
};
foreach (var req in requests)
manager.ProcessRequest(req);
| 類型 | 特徵 | 適用場景 |
|---|---|---|
| 純責任鏈 | 每個處理者要么完全處理,要么完全傳遞;請求必須被某處理者處理 | 嚴格分級授權、協定解析 |
| 不純責任鏈 | 處理者可部分處理後傳遞;請求可能無人處理 | 日誌記錄、中介軟體、事件處理 (最常見) |
請求可分派到多個分支 (如多租戶路由、多條件篩選):
public class BranchingHandler : Handler
{
private readonly List _branches = new();
public void AddBranch(Handler handler) => _branches.Add(handler);
public override void HandleRequest(Request request)
{
bool handled = false;
foreach (var branch in _branches)
{
if (branch.CanHandle(request))
{
branch.HandleRequest(request);
handled = true;
}
}
if (!handled && _successor != null)
_successor.HandleRequest(request);
}
}
public interface IAsyncHandler
{
Task HandleAsync(Request request, CancellationToken ct = default);
}
public abstract class AsyncHandlerBase : IAsyncHandler
{
private IAsyncHandler _next;
public IAsyncHandler SetNext(IAsyncHandler next) => _next = next;
public virtual async Task HandleAsync(Request request, CancellationToken ct = default)
{
if (_next != null)
await _next.HandleAsync(request, ct);
}
}
行為型模式是物件互動的藝術。責任鏈模式以「鏈式委派」解耦發送者與接收者,是 **中介軟體管線、審核流程、事件處理、異常處理** 的首選架構。現代 C# 結合介面、記錄、非同步、DI 容器,能建立極具彈性與可測試性的責任鏈系統。掌握 11 種行為型模式的適用場景,能讓你在面對複雜業務邏輯時,選擇最適合的協作模式,寫出清晰、可維護、可擴充的程式碼。