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

C# 設計模式實戰:裝飾者模式 - 動態擴展功能的優雅藝術

     編輯:關於C語言

前言:繼承的替代方案——「包裝」而非「繼承」

當需要為物件動態、透明地增加職責時,繼承會導致「子類別爆炸」且靜態固定。裝飾者模式採用組合 + 介面繼承,在運行時像剝洋蔥一樣層層包裝核心物件,實現靈活、可組合、可疊加的功能擴展。這正是「優先使用組合而非繼承」原則的經典落地。

一、 情境模擬:汪峰戰隊的動態組建

核心學員 (`Student`) 是基礎組件。導師 (`Director` 抽象裝飾者) 依序包裝:先加入男隊員 (`BoyTeam`),再加入女隊員 (`GirlTeam`),最後還能加「走秀」(`Show`) 等額外才藝。每層裝飾者都實作相同介面 (`Action`),對外透明。

裝飾者模式類圖 - 戰隊組建

1. 組件介面與核心實作

// 抽象組件
public abstract class TeamMember
{
    public abstract void Perform();
}

// 具體組件:基礎學員
public class Student : TeamMember
{
    public override void Perform()
    {
        Console.WriteLine("汪峰戰隊學員名單:");
    }
}

2. 抽象裝飾者:持有組件參考,轉發請求

public abstract class Director : TeamMember
{
    protected TeamMember _member;

    public void SetMember(TeamMember member) => _member = member;

    public override void Perform()
    {
        _member?.Perform(); // 轉發給被裝飾者
    }
}

3. 具體裝飾者:增加具體行為

// 裝飾者 A:男隊員 - 狂放風格
public class BoyTeam : Director
{
    public override void Perform()
    {
        base.Perform(); // 先執行內層 (學員名單)
        Console.WriteLine("我是汪峰戰隊,狂放型的!");
    }
}

// 裝飾者 B:女隊員 - 婉約風格 + 走秀才藝
public class GirlTeam : Director
{
    public override void Perform()
    {
        base.Perform(); // 執行內層 (可能已被 BoyTeam 裝飾)
        Console.WriteLine("我是汪峰戰隊,婉約型的。");
        Show(); // 新增行為
    }

    public void Show() => Console.WriteLine("婉約型才藝:T 台走秀");
}

4. 客戶端:動態組合裝飾鏈

static void Main()
{
    // 1. 核心物件
    TeamMember team = new Student();

    // 2. 動態包裝:順序決定執行順序
    TeamMember boy = new BoyTeam();
    boy.SetMember(team);       // 第 1 層:加男隊員

    TeamMember girl = new GirlTeam();
    girl.SetMember(boy);       // 第 2 層:加女隊員 (包含男隊員層)

    // 3. 執行完整鏈
    girl.Perform();

    /* 輸出順序:
       汪峰戰隊學員名單:      (Student)
       我是汪峰戰隊,狂放型的! (BoyTeam)
       我是汪峰戰隊,婉約型的。 (GirlTeam)
       婉約型才藝:T 台走秀     (GirlTeam 新增)
    */

    Console.ReadKey();
}

二、 .NET 框架經典實作:System.IO.Stream 家族

.NET IO 架構是裝飾者模式的教科書級應用。`Stream` 為抽象組件,`FileStream`/`MemoryStream` 為具體組件,`BufferedStream`、`CryptoStream`、`GZipStream` 為具體裝飾者。

.NET Stream 裝飾者結構

動態疊加功能範例

// 核心:記憶體流
MemoryStream memoryStream = new MemoryStream(new byte[] { 95, 96, 97, 98, 99 });

// 第 1 層:加入緩衝功能 (提升讀寫效能)
BufferedStream bufferedStream = new BufferedStream(memoryStream);

// 第 2 層:加入加密功能 (AES)
using (Aes aes = Aes.Create())
{
    CryptoStream cryptoStream = new CryptoStream(
        bufferedStream,           // 包裝緩衝流
        aes.CreateEncryptor(),
        CryptoStreamMode.Write
    );

    // 第 3 層:加入壓縮功能 (GZip) - 注意:這裡為示意,實際鏈需設計得當
    // GZipStream gzipStream = new GZipStream(cryptoStream, CompressionMode.Compress);

    // 寫入資料:會依序經過 GZip -> Crypto -> Buffered -> Memory
    // cryptoStream.Write(...);
}

關鍵觀察:客戶端代碼只依賴 `Stream` 抽象介面,完全不知情底層經過了多少層裝飾。新增功能只需「套一層新 Stream」,零修改既有代碼。

三、 模式優缺點與設計權衡

優點缺點
比靜態繼承更靈活:運行時動態增減行為 產生大量細小物件 (裝飾者實例),增加系統複雜度
避免繼承導致的類別爆炸 (單一職責原則) 過度使用難以除錯:物件外觀相同但行為不同
支援多重裝飾疊加,組合出海量行為變體 裝飾者與被裝飾者類型相同,但非完全相同,易混淆
符合開放-封閉原則:擴展功能無需修改核心類 若裝飾者需訪問被裝飾者具體方法,需轉型,破壞封裝

四、 適用場景判斷清單

  • [ ] 需在不影響其他物件的情況下,為單一物件動態添加職責。
  • [ ] 職責可動態撤銷 (移除裝飾層)。
  • [ ] 無法採用繼承 (類別為 `sealed`、或繼承會導致子類膨脹)。
  • [ ] 需由基本功能排列組合產生大量變體 (如視窗加滾動條+邊框+陰影+...)。

五、 進階話題:裝飾者 vs 代理 vs 適配器

  • 裝飾者:同介面,**增強**功能,支援疊加。
  • 代理:同介面,**控制存取** (延遲載入、權限、遠程),不增強業務邏輯。
  • 適配器:**轉換介面**,使不兼容介面能協作。

六、 總結

裝飾者模式以「薄包裝」實現「厚功能」,是應對「功能組合爆炸」的銀彈。核心在於:**所有裝飾者與核心組件實作同一介面,構成遞歸組合結構**。在 C# 中,`Stream` 體系、ASP.NET Core Middleware 管道、HttpClient `DelegatingHandler` 皆為其現代變體。掌握「識別核心職責、剝離可變飾行為、設計透明轉發鏈」三步曲,即可駕馭此模式。

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