當需要在不修改原有類別的前提下,動態為物件新增職責時,Decorator 模式是最佳選擇。它以組合取代繼承,提供比靜態繼承更彈性的功能擴展方式。
假設有一個檔案傳輸類別 FileTransfer,客戶端希望在上傳/下載前後加入存取檢查與日誌記錄。若直接修改類別或建立子類別,會導致類別爆炸且難以維護。
using System;
// Component 介面
public interface IFileTransfer
{
void Download(string url, byte[] data, int size);
void Upload(string url, byte[] data, int size);
}
// ConcreteComponent
public class FileTransfer : IFileTransfer
{
public void Download(string url, byte[] data, int size)
{
Console.WriteLine($"Downloading from {url}...");
// 實際下載邏輯
}
public void Upload(string url, byte[] data, int size)
{
Console.WriteLine($"Uploading to {url}...");
// 實際上傳邏輯
}
}
// Decorator 基底類別
public abstract class FileTransferDecorator : IFileTransfer
{
protected readonly IFileTransfer _inner;
protected FileTransferDecorator(IFileTransfer inner) => _inner = inner;
public virtual void Download(string url, byte[] data, int size) => _inner.Download(url, data, size);
public virtual void Upload(string url, byte[] data, int size) => _inner.Upload(url, data, size);
}
// ConcreteDecorator:存取控制與日誌
public class AccessControlDecorator : FileTransferDecorator
{
public AccessControlDecorator(IFileTransfer inner) : base(inner) { }
public override void Download(string url, byte[] data, int size)
{
if (!IsAccessAllowed(url))
{
Console.WriteLine($"Access denied: {url}");
return;
}
base.Download(url, data, size);
LogAccess(url);
}
public override void Upload(string url, byte[] data, int size)
{
if (!IsAccessAllowed(url))
{
Console.WriteLine($"Access denied: {url}");
return;
}
base.Upload(url, data, size);
LogAccess(url);
}
private bool IsAccessAllowed(string url)
{
// 實際授權邏輯
return true;
}
private void LogAccess(string url)
{
Console.WriteLine($"[LOG] Access to {url} at {DateTime.Now}");
}
}
// 另一個裝飾器:壓縮傳輸
public class CompressionDecorator : FileTransferDecorator
{
public CompressionDecorator(IFileTransfer inner) : base(inner) { }
public override void Download(string url, byte[] data, int size)
{
Console.WriteLine("Decompressing data...");
base.Download(url, data, size);
}
public override void Upload(string url, byte[] data, int size)
{
Console.WriteLine("Compressing data...");
base.Upload(url, data, size);
}
}
public class Program
{
public static void Main()
{
Console.Write("Enter URL: ");
string url = Console.ReadLine();
Console.Write("Enable logging & access check? (y/n): ");
bool enableLog = Console.ReadLine().Trim().ToLower() == "y";
Console.Write("Enable compression? (y/n): ");
bool enableCompress = Console.ReadLine().Trim().ToLower() == "y";
IFileTransfer transfer = new FileTransfer();
if (enableCompress)
transfer = new CompressionDecorator(transfer);
if (enableLog)
transfer = new AccessControlDecorator(transfer);
byte[] buffer = new byte[1024];
transfer.Download(url, buffer, buffer.Length);
}
}
Enter URL: https://example.com/file.zip Enable logging & access check? (y/n): y Enable compression? (y/n): y Compressing data... Downloading from https://example.com/file.zip... Decompressing data... [LOG] Access to https://example.com/file.zip at 2024-01-15 10:30:45
| 優點 | 說明 |
|---|---|
| 動態擴展 | 執行時任意組合裝飾器,無需建立大量子類別 |
| 單一職責 | 每個裝飾器專注單一附加功能 |
| 開放封閉 | 新增裝飾器無需修改既有元件 |
| 透明性 | 客戶端無需知曉是否被裝飾,介面一致 |
Decorator 模式以「包裝」取代「繼承」,實現功能的動態、透明擴展。.NET 框架中 System.IO.Stream 及其衍生類別正是此模式的經典應用。下一篇將介紹 Composite 組合模式。