當需要為物件動態、透明地增加職責時,繼承會導致「子類別爆炸」且靜態固定。裝飾者模式採用組合 + 介面繼承,在運行時像剝洋蔥一樣層層包裝核心物件,實現靈活、可組合、可疊加的功能擴展。這正是「優先使用組合而非繼承」原則的經典落地。
核心學員 (`Student`) 是基礎組件。導師 (`Director` 抽象裝飾者) 依序包裝:先加入男隊員 (`BoyTeam`),再加入女隊員 (`GirlTeam`),最後還能加「走秀」(`Show`) 等額外才藝。每層裝飾者都實作相同介面 (`Action`),對外透明。

// 抽象組件
public abstract class TeamMember
{
public abstract void Perform();
}
// 具體組件:基礎學員
public class Student : TeamMember
{
public override void Perform()
{
Console.WriteLine("汪峰戰隊學員名單:");
}
}
public abstract class Director : TeamMember
{
protected TeamMember _member;
public void SetMember(TeamMember member) => _member = member;
public override void Perform()
{
_member?.Perform(); // 轉發給被裝飾者
}
}
// 裝飾者 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 台走秀");
}
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();
}
System.IO.Stream 家族.NET IO 架構是裝飾者模式的教科書級應用。`Stream` 為抽象組件,`FileStream`/`MemoryStream` 為具體組件,`BufferedStream`、`CryptoStream`、`GZipStream` 為具體裝飾者。

// 核心:記憶體流
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」,零修改既有代碼。
| 優點 | 缺點 |
|---|---|
| 比靜態繼承更靈活:運行時動態增減行為 | 產生大量細小物件 (裝飾者實例),增加系統複雜度 |
| 避免繼承導致的類別爆炸 (單一職責原則) | 過度使用難以除錯:物件外觀相同但行為不同 |
| 支援多重裝飾疊加,組合出海量行為變體 | 裝飾者與被裝飾者類型相同,但非完全相同,易混淆 |
| 符合開放-封閉原則:擴展功能無需修改核心類 | 若裝飾者需訪問被裝飾者具體方法,需轉型,破壞封裝 |
裝飾者模式以「薄包裝」實現「厚功能」,是應對「功能組合爆炸」的銀彈。核心在於:**所有裝飾者與核心組件實作同一介面,構成遞歸組合結構**。在 C# 中,`Stream` 體系、ASP.NET Core Middleware 管道、HttpClient `DelegatingHandler` 皆為其現代變體。掌握「識別核心職責、剝離可變飾行為、設計透明轉發鏈」三步曲,即可駕馭此模式。