首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> C# >> C# 入門

用設計模式固化你的 C# 程式(三):Decorator 裝飾者模式

     編輯:C# 入門

前言

當需要在不修改原有類別的前提下,動態為物件新增職責時,Decorator 模式是最佳選擇。它以組合取代繼承,提供比靜態繼承更彈性的功能擴展方式。

動機場景

假設有一個檔案傳輸類別 FileTransfer,客戶端希望在上傳/下載前後加入存取檢查與日誌記錄。若直接修改類別或建立子類別,會導致類別爆炸且難以維護。

模式結構

  • Component (介面/抽象類別):定義核心操作介面。
  • ConcreteComponent:實作核心功能。
  • Decorator:實作 Component 介面,內含 Component 參考,委派核心操作並在前後添加行為。
  • ConcreteDecorator:具體裝飾類別,實作特定附加功能。

C# 實作:檔案傳輸與存取控制

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

優點與適用場景

優點說明
動態擴展執行時任意組合裝飾器,無需建立大量子類別
單一職責每個裝飾器專注單一附加功能
開放封閉新增裝飾器無需修改既有元件
透明性客戶端無需知曉是否被裝飾,介面一致

適用場景

  • 需要在不影響其他物件的情況下,為單一物件動態添加職責。
  • 無法使用繼承(如類別密封、或會導致子類別爆炸)。
  • 附加功能可選、可疊加(如 IO 流:BufferedStream, CryptoStream, GZipStream)。

注意事項

  1. 裝飾器與被裝飾物件共享相同介面,但型別不同,避免過度依賴具體型別。
  2. 過多層級包裝可能增加除錯難度,建議配合 DI 容器管理組合。
  3. 若裝飾器需存取被裝飾物件的特定成員,可透過模式匹配或引入額外介面。

總結

Decorator 模式以「包裝」取代「繼承」,實現功能的動態、透明擴展。.NET 框架中 System.IO.Stream 及其衍生類別正是此模式的經典應用。下一篇將介紹 Composite 組合模式。

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