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

設計模式 C# 實作:裝飾者模式完整解析與應用

     編輯:關於C語言

裝飾者模式

動態地給物件新增職責,且不改變其結構。比繼承更靈活,是「包裝」的替代方案。

適用場景

  • 需要在不建立大量子類別的情況下擴充功能
  • 職責可動態新增/移除 (如視窗加捲軸、邊框、陰影)
  • 基本功能的排列組合產生大量變體 (咖啡調味、串流處理管線)
  • 避免「繼承爆炸」:BaseWindow → ScrollableWindow → BorderedScrollableWindow → ...

UML 角色對應

角色C# 實作說明
Component (抽象元件)介面或抽象類別定義核心合約
ConcreteComponent (具體元件)實作介面的類別被裝飾的核心物件
Decorator (裝飾者基類)實作介面、持有 Component 參考轉發呼叫、定義裝飾合約
ConcreteDecorator (具體裝飾者)繼承 Decorator、新增行為具體功能擴充

經典實作:咖啡飲料系統

1. 定義元件介面


public interface IBeverage
{
    string Description { get; }
    decimal Cost();
}

2. 具體元件:基礎飲料


public class Espresso : IBeverage
{
    public string Description => "義式濃縮";
    public decimal Cost() => 50m;
}

public class HouseBlend : IBeverage
{
    public string Description => "招牌配方";
    public decimal Cost() => 60m;
}

public class DarkRoast : IBeverage
{
    public string Description => "深烘焙";
    public decimal Cost() => 55m;
}

3. 裝飾者基類:持有被裝飾物件


public abstract class BeverageDecorator : IBeverage
{
    protected readonly IBeverage _beverage;
    
    protected BeverageDecorator(IBeverage beverage)
    {
        _beverage = beverage ?? throw new ArgumentNullException(nameof(beverage));
    }
    
    public virtual string Description => _beverage.Description;
    public virtual decimal Cost() => _beverage.Cost();
}

4. 具體裝飾者:調味料


public class Mocha : BeverageDecorator
{
    public Mocha(IBeverage beverage) : base(beverage) { }
    
    public override string Description => $"{_beverage.Description}, 摩卡";
    public override decimal Cost() => _beverage.Cost() + 10m;
}

public class Soy : BeverageDecorator
{
    public Soy(IBeverage beverage) : base(beverage) { }
    
    public override string Description => $"{_beverage.Description}, 豆漿";
    public override decimal Cost() => _beverage.Cost() + 8m;
}

public class Whip : BeverageDecorator
{
    public Whip(IBeverage beverage) : base(beverage) { }
    
    public override string Description => $"{_beverage.Description}, 鮮奶油";
    public override decimal Cost() => _beverage.Cost() + 5m;
}

5. 用戶端組合


public class CoffeeShop
{
    public static void Main()
    {
        // 訂單 1:深烘焙 + 雙摩卡 + 鮮奶油
        IBeverage drink1 = new DarkRoast();
        drink1 = new Mocha(drink1);
        drink1 = new Mocha(drink1);
        drink1 = new Whip(drink1);
        
        Console.WriteLine($"{drink1.Description} - ${drink1.Cost()}");
        // 輸出:深烘焙, 摩卡, 摩卡, 鮮奶油 - $75
        
        // 訂單 2:招牌配方 + 豆漿 + 摩卡 + 鮮奶油
        IBeverage drink2 = new HouseBlend();
        drink2 = new Soy(drink2);
        drink2 = new Mocha(drink2);
        drink2 = new Whip(drink2);
        
        Console.WriteLine($"{drink2.Description} - ${drink2.Cost()}");
        // 輸出:招牌配方, 豆漿, 摩卡, 鮮奶油 - $83
    }
}

.NET Framework 內建裝飾者範例

基礎類別裝飾者新增功能
StreamBufferedStream緩衝讀寫提升效能
StreamCryptoStream加密/解密
StreamGZipStream壓縮/解壓縮
StreamStreamReader/Writer文字讀寫
TextReaderStringReader字串作為輸入來源

// 串流管線組合:檔案 → 緩衝 → GZip 壓縮 → 加密
using (FileStream fs = File.Create("data.enc"))
using (CryptoStream cs = new CryptoStream(fs, encryptor, CryptoStreamMode.Write))
using (GZipStream gz = new GZipStream(cs, CompressionMode.Compress))
using (StreamWriter sw = new StreamWriter(gz))
{
    sw.WriteLine("敏感資料將被壓縮並加密儲存");
}

進階:透明裝飾者 vs 半透明裝飾者

類型特徵範例
透明介面不變,用戶端不知情Stream 裝飾者、上述咖啡範例
半透明新增額外介面/方法視窗裝飾者新增 SetScrollBar()

// 半透明裝飾者:新增特定功能介面
public interface IScrollable
{
    void ScrollTo(int x, int y);
}

public class ScrollableWindow : WindowDecorator, IScrollable
{
    public ScrollableWindow(IWindow window) : base(window) { }
    
    public void ScrollTo(int x, int y) { /* 實作捲動 */ }
    
    public override void Draw() 
    {
        base.Draw();
        DrawScrollBars();
    }
}

裝飾者 vs 繼承 vs 策略模式

維度繼承裝飾者策略模式
靈活度編譯期固定執行期動態組合執行期可切換演算法
組合數類別爆炸線性包裝策略類別獨立
狀態共享子類別擁有自身狀態每層可有獨立狀態策略物件封裝狀態
型別身分是基類型別是基類型別 (透明)持有策略介面

最佳實務與注意事項

  • 保持介面單一:裝飾者只擴充單一職責,避免巨大裝飾者
  • 避免過度包裝:超過 3-4 層難以偵錯,考慮重構
  • 實作 IDisposable 傳遞:若被裝飾物件可釋放,裝飾者應轉發 Dispose()
  • 序列化考量:深層包裝物件序列化複雜,需自訂
  • 不要破壞 LSP:裝飾者必須完全符合元件介面契約

本章小結

  • 裝飾者模式以「包裝」取代「繼承」,實現動態功能組合
  • 核心結構:Component 介面 → ConcreteComponent → Decorator (持有 Component) → ConcreteDecorator
  • .NET Stream 體系是教科書級裝飾者應用
  • 適用於功能疊加、管線處理、UI 構件增強等場景
  • 現代 C# 也可用 Func<T, T> 管線或 Middleware 模式實現類似效果
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved