索引
- Clone
用原型實例指定創建對象的種類,并且通過拷貝這些原型創建新的對象。
Specify the kinds of objects to create using a prototypical instance, and create new objects by copying this prototype.
Prototype
- 聲明一個克隆自身的接口。
ConcretePrototype
- 實現一個克隆自身的操作。
Client
- 讓一個原型克隆自身從而創建一個新的對象。
在以下情況下可以使用 Prototype 模式:
- 一個系統要獨立于它的產品的創建、構成和表示時。
- 當要實例化的類是在運行時刻指定時,例如:通過動態裝載。
- 為了避免創建一個與產品類層次平行的工廠類層次時。
- 當一個類的實例只能有幾個不同狀態組合中的一種時。建立相應數目的原型并克隆它們可能比每次用合適的狀態手工實例化該類更方便一些。
- 每一個 Prototype 子類都必須實現 Clone 操作。當內部包括一些不支持拷貝或有循環引用的對象時,實現克隆可能也會很困難。
- 它對客戶隱藏了具體的產品類,因此減少了客戶知道的名字的數目。
- 使客戶無需改變即可使用與特定應用相關的類。
- 運行時刻增加和刪除產品。
- 改變值以指定新對象。
- 改變結構以指定新對象。
- 減少子類的構造。
- 用類動態配置應用。
- Abstract Factory 可以用 Prototype 來實現。
- Composite 和 Decorator 模式的設計也可以從 Prototype 獲益。
使用命名約定是一個好習慣,例如,總是聲明那些實現克隆的操作為 Clone()。
當一個系統中原型數目不固定時,可以保持一個可用原型的注冊表,用以存儲和檢索原型。我們稱這個注冊表為原型管理器(Prototype Manager)。
客戶在克隆一個原型前會先向注冊表請求該原型。
1 namespace PrototypePattern.Implementation1 2 { 3 public abstract class AbstractOrInterfaceOfPrototypeProduct 4 { 5 public int ValueProperty1 { get; set; } 6 7 public abstract AbstractOrInterfaceOfPrototypeProduct Clone(); 8 } 9 10 public class ConcretePrototypeProductA : AbstractOrInterfaceOfPrototypeProduct 11 { 12 public override AbstractOrInterfaceOfPrototypeProduct Clone() 13 { 14 return new ConcretePrototypeProductA() 15 { 16 ValueProperty1 = this.ValueProperty1, 17 }; 18 } 19 } 20 21 public class ConcretePrototypeProductB : AbstractOrInterfaceOfPrototypeProduct 22 { 23 public override AbstractOrInterfaceOfPrototypeProduct Clone() 24 { 25 return new ConcretePrototypeProductB() 26 { 27 ValueProperty1 = this.ValueProperty1, 28 }; 29 } 30 } 31 32 public class ProductPrototypeManager 33 { 34 private Dictionary<string, AbstractOrInterfaceOfPrototypeProduct> _registry 35 = new Dictionary<string, AbstractOrInterfaceOfPrototypeProduct>(); 36 37 public void Register(string name, 38 AbstractOrInterfaceOfPrototypeProduct prototypeProduct) 39 { 40 _registry[name] = prototypeProduct; 41 } 42 43 public void Unregister(string name) 44 { 45 _registry.Remove(name); 46 } 47 48 public AbstractOrInterfaceOfPrototypeProduct Retrieve(string name) 49 { 50 return _registry[name]; 51 } 52 53 public bool IsRegisterd(string name) 54 { 55 return _registry.ContainsKey(name); 56 } 57 } 58 59 public class Client 60 { 61 public void TestCase1() 62 { 63 AbstractOrInterfaceOfPrototypeProduct prototypeProduct1 = new ConcretePrototypeProductA(); 64 AbstractOrInterfaceOfPrototypeProduct prototypeProduct2 = new ConcretePrototypeProductB(); 65 66 ProductPrototypeManager manager = new ProductPrototypeManager(); 67 manager.Register("PrototypeProduct1", prototypeProduct1); 68 manager.Register("PrototypeProduct2", prototypeProduct2); 69 70 AbstractOrInterfaceOfPrototypeProduct clonedProduct1 = manager.Retrieve("PrototypeProduct1").Clone(); 71 72 if (manager.IsRegisterd("PrototypeProduct2")) 73 { 74 AbstractOrInterfaceOfPrototypeProduct clonedProduct2 = manager.Retrieve("PrototypeProduct2").Clone(); 75 } 76 } 77 } 78 }
Prototype 模式最困難的部分在于正確的實現 Clone 操作。
淺拷貝(Shallow Copy)在拷貝時只復制對象所有字段的值。如果字段是值類型,則復制其值;如果字段是引用類型,則復制引用指針。
1 namespace PrototypePattern.Implementation2 2 { 3 public class ReferencedClass 4 { 5 public int ReferencedClassProperty1 { get; set; } 6 } 7 8 public abstract class AbstractOrInterfaceOfPrototypeProduct 9 { 10 public int ValueProperty1 { get; set; } 11 public ReferencedClass ReferenceProperty2 { get; set; } 12 13 public abstract AbstractOrInterfaceOfPrototypeProduct Clone(); 14 } 15 16 public class ConcreteShallowCopyPrototypeProductA 17 : AbstractOrInterfaceOfPrototypeProduct 18 { 19 public ConcreteShallowCopyPrototypeProductA() 20 { 21 this.ReferenceProperty2 = new ReferencedClass() 22 { 23 ReferencedClassProperty1 = 111 24 }; 25 } 26 27 public override AbstractOrInterfaceOfPrototypeProduct Clone() 28 { 29 return new ConcreteShallowCopyPrototypeProductA() 30 { 31 ValueProperty1 = this.ValueProperty1, 32 ReferenceProperty2 = this.ReferenceProperty2, 33 }; 34 } 35 } 36 37 public class Client 38 { 39 public void TestCase2() 40 { 41 AbstractOrInterfaceOfPrototypeProduct prototypeProduct1 = new ConcreteShallowCopyPrototypeProductA(); 42 AbstractOrInterfaceOfPrototypeProduct clonedProduct1 = prototypeProduct1.Clone(); 43 bool areEqual1 = object.ReferenceEquals( 44 prototypeProduct1.ReferenceProperty2, 45 clonedProduct1.ReferenceProperty2); 46 } 47 } 48 }
深拷貝(Deep Copy)涉及對源對象整個結構的拷貝。
深拷貝在拷貝時復制對象的所有字段的值。如果字段是值類型,則復制其值;如果字段是引用類型,則會將這個引用指針指向的對象也克隆一份。
可以通過序列化和反序列化來實現深拷貝。
1 namespace PrototypePattern.Implementation3 2 { 3 public class ReferencedClass 4 { 5 public int ReferencedClassProperty1 { get; set; } 6 } 7 8 public abstract class AbstractOrInterfaceOfPrototypeProduct 9 { 10 public int ValueProperty1 { get; set; } 11 public ReferencedClass ReferenceProperty2 { get; set; } 12 13 public abstract AbstractOrInterfaceOfPrototypeProduct Clone(); 14 } 15 16 public class ConcreteShallowCopyPrototypeProductA 17 : AbstractOrInterfaceOfPrototypeProduct 18 { 19 public ConcreteShallowCopyPrototypeProductA() 20 { 21 this.ReferenceProperty2 = new ReferencedClass() 22 { 23 ReferencedClassProperty1 = 111 24 }; 25 } 26 27 public override AbstractOrInterfaceOfPrototypeProduct Clone() 28 { 29 return new ConcreteShallowCopyPrototypeProductA() 30 { 31 ValueProperty1 = this.ValueProperty1, 32 ReferenceProperty2 = this.ReferenceProperty2, 33 }; 34 } 35 } 36 37 public class ConcreteDeepCopyPrototypeProductB 38 : AbstractOrInterfaceOfPrototypeProduct 39 { 40 public ConcreteDeepCopyPrototypeProductB() 41 { 42 this.ReferenceProperty2 = new ReferencedClass() 43 { 44 ReferencedClassProperty1 = 222 45 }; 46 } 47 48 public override AbstractOrInterfaceOfPrototypeProduct Clone() 49 { 50 return new ConcreteDeepCopyPrototypeProductB() 51 { 52 ValueProperty1 = this.ValueProperty1, 53 ReferenceProperty2 = new ReferencedClass() 54 { 55 ReferencedClassProperty1 = 56 this.ReferenceProperty2.ReferencedClassProperty1 57 }, 58 }; 59 } 60 } 61 62 public class Client 63 { 64 public void TestCase3() 65 { 66 AbstractOrInterfaceOfPrototypeProduct prototypeProduct1 = new ConcreteShallowCopyPrototypeProductA(); 67 AbstractOrInterfaceOfPrototypeProduct clonedProduct1 = prototypeProduct1.Clone(); 68 bool areEqual1 = object.ReferenceEquals( 69 prototypeProduct1.ReferenceProperty2, 70 clonedProduct1.ReferenceProperty2); 71 72 AbstractOrInterfaceOfPrototypeProduct prototypeProduct2 = new ConcreteDeepCopyPrototypeProductB(); 73 AbstractOrInterfaceOfPrototypeProduct clonedProduct2 = prototypeProduct2.Clone(); 74 bool areEqual2 = object.ReferenceEquals( 75 prototypeProduct2.ReferenceProperty2, 76 clonedProduct2.ReferenceProperty2); 77 78 Console.WriteLine("{0}, {1}", areEqual1, areEqual2); 79 } 80 } 81 }
客戶可能會希望使用一些值來初始化該對象的內部狀態。
但在 Clone 操作中傳遞參數會破壞克隆接口的統一性。
原型的類可以在 Clone 操作之后,調用包含初始化參數的 Initialize 方法來設定對象內部狀態。
1 namespace PrototypePattern.Implementation4 2 { 3 public class ReferencedClass 4 { 5 public int ReferencedClassProperty1 { get; set; } 6 } 7 8 public abstract class AbstractOrInterfaceOfPrototypeProduct 9 { 10 public int ValueProperty1 { get; set; } 11 public ReferencedClass ReferenceProperty2 { get; set; } 12 13 public abstract AbstractOrInterfaceOfPrototypeProduct Clone(); 14 } 15 16 public class ConcreteDeepCopyPrototypeProductB 17 : AbstractOrInterfaceOfPrototypeProduct 18 { 19 public ConcreteDeepCopyPrototypeProductB() 20 { 21 } 22 23 public void Initialize(int propertyValue) 24 { 25 this.ValueProperty1 = propertyValue; 26 this.ReferenceProperty2.ReferencedClassProperty1 = propertyValue; 27 } 28 29 public override AbstractOrInterfaceOfPrototypeProduct Clone() 30 { 31 return new ConcreteDeepCopyPrototypeProductB() 32 { 33 ValueProperty1 = this.ValueProperty1, 34 ReferenceProperty2 = new ReferencedClass() 35 { 36 ReferencedClassProperty1 = 37 this.ReferenceProperty2.ReferencedClassProperty1 38 }, 39 }; 40 } 41 } 42 43 public class Client 44 { 45 public void TestCase4() 46 { 47 AbstractOrInterfaceOfPrototypeProduct prototypeProduct2 = new ConcreteDeepCopyPrototypeProductB(); 48 ConcreteDeepCopyPrototypeProductB clonedProduct2 = 49 (ConcreteDeepCopyPrototypeProductB)prototypeProduct2.Clone(); 50 51 clonedProduct2.Initialize(123); 52 } 53 } 54 }
《設計模式之美》為 Dennis Gao 發布于博客園的系列文章,任何未經作者本人同意的人為或爬蟲轉載均為耍流氓。
文章列表