首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> C# >> 關於C#

C# 設計模式深度解析:行為型模式總覽與責任鏈模式完整實作

     編輯:關於C#

前言

行為型設計模式 關注物件間的**責任分配**與**演算法封裝**,核心在於「物件如何互動」而非「物件結構如何組成」。GoF 定義了 11 種行為型模式,本文提供完整分類概覽,並深度解析最實用的 **責任鏈模式**。

行為型模式分類總覽

模式核心意圖關鍵詞常見應用
責任鏈讓多個物件有機會處理請求,避免發送者與接收者耦合鏈式、遞交、動態組合權限驗證、日誌處理、異常處理、中介軟體管線
命令將請求封裝為物件,支援撤銷、佇列、記錄封裝、參數化、可撤銷GUI 按鈕、巨集錄製、交易系統、工作佇列
解譯器定義語言文法並解譯執行文法、AST、解譯器規則引擎、查詢語言、腳本引擎、正則運算式
迭代器統一存取聚合物件元素,不暴露內部結構遍歷、封裝、多態IEnumerable、集合遍歷、樹狀結構走訪
中介者集中管理物件間互動,降低耦合集中、解耦、協調表單驗證、聊天室、航空管制、MVC Controller
備忘錄捕獲物件內部狀態以便稍後恢復快照、狀態、撤銷編輯器撤銷、遊戲存檔、資料庫交易回滾
觀察者定義一對多依賴,狀態變更自動通知發布/訂閱、事件、反應式事件系統、資料繫結、訊息佇列、React/Vue 響應式
狀態允許物件在內部狀態改變時改變行為狀態機、行為變化、封裝訂單流程、遊戲角色狀態、TCP 連線狀態、工作流
策略定義演算法族群,封裝並可互換演算法、可換、參數化排序策略、支付方式、壓縮演算法、路由演算法
模板方法定義演算法骨架,延遲步驟至子類別骨架、鉤子、好萊塢原則資料存取模板、報表產生、演算法框架、單元測試基類
訪問者在不修改類別前提下新增操作雙重分派、操作分離編譯器 AST 走訪、報表匯出、物件序列化

深度解析:責任鏈模式

模式動機

避免「巨大的 if-else / switch 鏈」處理請求,將處理邏輯分散到獨立的處理器物件,形成鏈結。請求沿鏈傳遞,直到某處理器處理或鏈結結束。

結構角色

  • Handler (抽象處理者):定義處理請求的介面,包含 `SetSuccessor` 與 `HandleRequest`
  • ConcreteHandler (具體處理者):決定是否處理請求,否則傳遞給下一個
  • Client (客戶端):建立鏈結並發起請求

標準實作


// 抽象處理者
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);
}

現代 C# 改良版:使用介面 + 建構子注入


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 中介軟體管線即責任鏈


// 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);

純責任鏈 vs 不純責任鏈

類型特徵適用場景
純責任鏈每個處理者要么完全處理,要么完全傳遞;請求必須被某處理者處理嚴格分級授權、協定解析
不純責任鏈處理者可部分處理後傳遞;請求可能無人處理日誌記錄、中介軟體、事件處理 (最常見)

進階變體

樹狀責任鏈

請求可分派到多個分支 (如多租戶路由、多條件篩選):


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);
    }
}

最佳實務與反模式

✅ 最佳實務

  • 單一職責:每個處理者只負責一類邏輯
  • 明確終止條件:避免無限遞迴或請求丟失
  • 不可變鏈結:建立後不動態修改 (除非有明確需求)
  • 日誌追蹤:記錄請求流經路徑,利於除錯
  • 效能考量:熱路徑避免過長鏈結,考慮快取或索引

❌ 反模式

  • 上帝處理者:單一處理者包含大量 if-else,退化為巨大函數
  • 循環鏈結:誤設定導致無限迴圈
  • 隱性依賴:處理者間共享可變狀態,破壞獨立性
  • 過度設計:簡單邏輯硬套責任鏈 (YAGNI)

總結

行為型模式是物件互動的藝術。責任鏈模式以「鏈式委派」解耦發送者與接收者,是 **中介軟體管線、審核流程、事件處理、異常處理** 的首選架構。現代 C# 結合介面、記錄、非同步、DI 容器,能建立極具彈性與可測試性的責任鏈系統。掌握 11 種行為型模式的適用場景,能讓你在面對複雜業務邏輯時,選擇最適合的協作模式,寫出清晰、可維護、可擴充的程式碼。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved