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

C# 設計模式實戰:原型模式與淺/深拷貝的完整解析

     編輯:關於C語言

前言:當 new 成為效能瓶頸

在系統中,若某類別的實例化過程極其複雜(如需讀取大量配置、資料庫查詢、複雜運算),且需要大量相似實例時,反覆 new 不僅效能低落,還增加記憶體壓力。原型模式透過複製現有實例來建立新物件,避免重複初始化開銷,是解決此類問題的利器。

一、 情境模擬:考試系統的試卷列印與作答

「樣卷」是原型,「複印機」負責克隆,「學生」在各自試卷上作答。關鍵在於:學生修改自己的試卷不應影響他人,這直接牽涉到淺拷貝與深拷貝的差異。

原型模式類圖 - 考試範例

二、 核心程式碼實作:從淺拷貝到深拷貝

1. 定義原型介面與參考類別

// 附加題:引用型別,用於驗證深/淺拷貝
public class ExtraQuestion
{
    public string SpouseAge { get; set; }
}

// 原型介面
public interface IExamPaper : ICloneable
{
    string KnowDesignPatterns { get; set; }
    string PatternCount { get; set; }
    string KnownPatterns { get; set; }
    ExtraQuestion Extra { get; set; }
}

2. 具體原型:使用 MemberwiseClone 實作淺拷貝

public class ExamPaper : IExamPaper
{
    private string _knowDesignPatterns;
    private string _patternCount;
    private string _knownPatterns;
    private ExtraQuestion _extra = new ExtraQuestion();

    public string KnowDesignPatterns { get => _knowDesignPatterns; set => _knowDesignPatterns = value; }
    public string PatternCount { get => _patternCount; set => _patternCount = value; }
    public string KnownPatterns { get => _knownPatterns; set => _knownPatterns = value; }
    public ExtraQuestion Extra { get => _extra; set => _extra = value; }

    // 預設淺拷貝:值型別複製,引用型別共用記憶體位址
    public object Clone() => this.MemberwiseClone();
}

3. 客戶端測試:揭示淺拷貝的陷阱

static void Main()
{
    // 1. 建立樣卷 (原型)
    IExamPaper master = new ExamPaper();

    // 2. 複印兩份
    IExamPaper studentA = (IExamPaper)master.Clone();
    IExamPaper studentB = (IExamPaper)master.Clone();

    // 3. 學生作答 (修改值型別屬性)
    studentA.PatternCount = "23";
    studentB.PatternCount = "24";

    // 4. 修改引用型別屬性 (附加題)
    studentA.Extra.SpouseAge = "18";
    studentB.Extra.SpouseAge = "20";

    // 5. 驗證結果
    Console.WriteLine($"學生A 設計模式幾種: {studentA.PatternCount}"); // 23
    Console.WriteLine($"學生B 設計模式幾種: {studentB.PatternCount}"); // 24 正常
    Console.WriteLine($"學生A 附加題年齡: {studentA.Extra.SpouseAge}");   // 20 錯誤!被B覆蓋
    Console.WriteLine($"學生B 附加題年齡: {studentB.Extra.SpouseAge}");   // 20

    Console.ReadKey();
}

現象:值型別 (`string`) 獨立,引用型別 (`ExtraQuestion`) 共用同一記憶體,導致資料污染。

4. 解決方案一:手動實作深拷貝

public object Clone()
{
    // 1. 先淺拷貝本身
    ExamPaper clone = (ExamPaper)this.MemberwiseClone();
    // 2. 手動新建引用型別屬性
    clone.Extra = new ExtraQuestion { SpouseAge = this.Extra.SpouseAge };
    return clone;
}

5. 解決方案二:序列化反序列化 (通用深拷貝)

適用於物件圖複雜、含循環引用、或不想手動維護拷貝邏輯的場景。

[Serializable] // 必須標記
public class ExamPaper : IExamPaper
{
    // ... 屬性同前 ...

    public object Clone()
    {
        using (MemoryStream ms = new MemoryStream())
        {
            BinaryFormatter formatter = new BinaryFormatter();
            formatter.Serialize(ms, this);
            ms.Position = 0;
            return formatter.Deserialize(ms);
        }
    }
}

// 輔助類 (可選,封裝 Base64 字串儲存)
public static class SerializationHelper
{
    public static string Serialize(object obj) { ... }
    public static T Deserialize(string data) { ... }
}

原型模式標準 UML 類圖

三、 模式優缺點與適用場景

優點缺點
隱藏建立複雜度,客戶端無需知曉具體建構細節 每個類別需實作 Clone 方法,侵入性較強
動態增減產品類,無需建立平行的工廠階層 深拷貝實作複雜 (循環引用、非序列化物件)
比工廠方法更簡化建立結構 過度使用導致大量細小物件,除錯困難

適用場景:類別初始化昂貴、需大量相似實例、物件狀態組合較固定、需動態增減行為組合。

四、 .NET 框架實戰:ICloneable 介面

.NET 基礎類別庫 (BCL) 中,System.ICloneable 即為原型模式的標準化定義。實作該介面的類別 (如 Array, DataTable, 自訂類別) 即擁有複製能力。使用反編譯工具 (如 ILSpy, dotPeek) 可發現大量 BCL 類別實作此介面。

.NET 中實作 ICloneable 的類別

五、 總結

原型模式以「複製」替代「新建」,在特定場景下極大提升效能與靈活性。核心難點在於深拷貝的正確實作:簡單物件用 MemberwiseClone + 手動複製引用欄位;複雜物件圖首選序列化方案。掌握淺/深拷貝邊界,是駕馭此模式的關鍵。

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