在物件導向設計中,我們常透過繼承來擴充功能。但繼承帶來的是靜態、編譯期決定的類別階層——當擴充功能呈排列組合爆炸式增長時,子類別數量將失控膨脹(經典的「子類別復子類別,子類別何其多」)。裝飾模式(Decorator Pattern)提供了一條优雅的出路:以物件組合取代類別繼承,在執行期動態為單一物件附加職責。
GoF 定義:動態地給一個物件添加一些額外的職責。就增加功能來說,Decorator 模式相比生成子類別更為靈活。
生活中的經典類比是「畫框與畫作」:

假設我們有兩種基礎日誌實作:DatabaseLog、TextFileLog。需求新增「錯誤等級記錄」、「優先級記錄」兩個維度的擴充功能。若用繼承:
// 單一擴充功能:4 個子類別
DBErrorLog : DatabaseLog, IError
DBPriorityLog : DatabaseLog, IPriority
TFErrorLog : TextFileLog, IError
TFPriorityLog : TextFileLog, IPriority
// 組合擴充功能:再新增 4 個子類別
DBEPLog : DatabaseLog, IError, IPriority
TFEPLog : TextFileLog, IError, IPriority

問題顯而易見:
關鍵洞見:將擴充功能封裝為獨立的「包裝器」類別,持有被裝飾物件的參考,並在轉呼叫前後插入額外邏輯。
public abstract class LogWrapper : Log {
protected Log _wrappedLog;
public LogWrapper(Log log) { _wrappedLog = log; }
public override void Write(string log) => _wrappedLog.Write(log); // 預設轉呼叫
}
public class LogErrorWrapper : LogWrapper {
public LogErrorWrapper(Log log) : base(log) { }
public override void Write(string log) {
SetError(); // 前置:新增錯誤等級邏輯
base.Write(log); // 轉呼叫被裝飾物件
}
private void SetError() { /* 記錄錯誤等級實作 */ }
}
public class LogPriorityWrapper : LogWrapper {
public LogPriorityWrapper(Log log) : base(log) { }
public override void Write(string log) {
SetPriority(); // 前置:新增優先級邏輯
base.Write(log);
}
private void SetPriority() { /* 記錄優先級實作 */ }
}

public class Program {
public static void Main() {
Log log = new DatabaseLog();
// 單一功能裝飾
Log errorLog = new LogErrorWrapper(log);
errorLog.Write("Message"); // 僅含錯誤等級
// 多重裝飾:關鍵在於巢狀包裝!
Log priorityLog = new LogPriorityWrapper(log);
priorityLog.Write("Message"); // 僅含優先級
// 組合功能:先裝飾錯誤等級,再裝飾優先級
Log combined = new LogPriorityWrapper(new LogErrorWrapper(log));
combined.Write("Message"); // 同時擁有錯誤等級與優先級
}
}
精妙之處:LogPriorityWrapper 的建構子接受 Log 型別,而 LogErrorWrapper 繼承自 LogWrapper → Log,因此可作為參數傳入。這形成了「洋蔥狀」的包裝鏈,每層裝飾器只關注單一職責,執行順序由包裝順序決定。
.NET 的 System.IO.Stream 及其衍生類別是裝飾模式的標準實現(省略了抽象裝飾角色):
Stream(抽象基類)MemoryStream、FileStream、NetworkStreamBufferedStream(緩衝)、CryptoStream(加密)、GZipStream(壓縮)
MemoryStream ms = new MemoryStream(data);
BufferedStream buffered = new BufferedStream(ms); // 新增緩衝功能
CryptoStream encrypted = new CryptoStream(buffered, ...); // 再新增加密功能
// encrypted 同時擁有記憶體儲存、緩衝、加密三重能力
反編譯 BufferedStream 可見其核心結構:
public sealed class BufferedStream : Stream {
private Stream _s; // 被裝飾的 Stream
private int _bufferSize;
public BufferedStream(Stream stream) { _s = stream; }
public override void Write(byte[] buffer, int offset, int count) {
// 緩衝邏輯...
_s.Write(buffer, offset, count); // 轉呼叫
}
}
資料存取應用程式區塊(DAAB)中的 DbCommandWrapper 包裝 IDbCommand,統一提供參數處理、執行重試等横切關注點:

GoF 指出裝飾模式可根據情境簡化:
ConcreteComponent,裝飾類可直接繼承它 
Decorator 與 ConcreteDecorator 為單一類別 
decorated is ConcreteComponent 為 false)裝飾模式以「包裝」取代「繼承」,將「什麼時候擴充功能」的決定權從編譯期移交給執行期。它不是為了解決多重繼承問題,而是為了解決主體類別在多個方向上的擴充功能組合問題。在 .NET 生態中,從 Stream 到 ASP.NET Core Middleware 管線、Entity Framework 攔截器,裝飾模式的身影無處不在。掌握它,意味著你擁有了應對「功能組合爆炸」的利器。