在系統中,若某類別的實例化過程極其複雜(如需讀取大量配置、資料庫查詢、複雜運算),且需要大量相似實例時,反覆 new 不僅效能低落,還增加記憶體壓力。原型模式透過複製現有實例來建立新物件,避免重複初始化開銷,是解決此類問題的利器。
「樣卷」是原型,「複印機」負責克隆,「學生」在各自試卷上作答。關鍵在於:學生修改自己的試卷不應影響他人,這直接牽涉到淺拷貝與深拷貝的差異。

// 附加題:引用型別,用於驗證深/淺拷貝
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; }
}
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();
}
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`) 共用同一記憶體,導致資料污染。
public object Clone()
{
// 1. 先淺拷貝本身
ExamPaper clone = (ExamPaper)this.MemberwiseClone();
// 2. 手動新建引用型別屬性
clone.Extra = new ExtraQuestion { SpouseAge = this.Extra.SpouseAge };
return clone;
}
適用於物件圖複雜、含循環引用、或不想手動維護拷貝邏輯的場景。
[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) { ... }
}

| 優點 | 缺點 |
|---|---|
| 隱藏建立複雜度,客戶端無需知曉具體建構細節 | 每個類別需實作 Clone 方法,侵入性較強 |
| 動態增減產品類,無需建立平行的工廠階層 | 深拷貝實作複雜 (循環引用、非序列化物件) |
| 比工廠方法更簡化建立結構 | 過度使用導致大量細小物件,除錯困難 |
適用場景:類別初始化昂貴、需大量相似實例、物件狀態組合較固定、需動態增減行為組合。
ICloneable 介面.NET 基礎類別庫 (BCL) 中,System.ICloneable 即為原型模式的標準化定義。實作該介面的類別 (如 Array, DataTable, 自訂類別) 即擁有複製能力。使用反編譯工具 (如 ILSpy, dotPeek) 可發現大量 BCL 類別實作此介面。

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