更新时间:2022-08-12 21:20:36
索引
用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。
Specify the kinds of objects to create using a prototypical instance, and create new objects by copying this prototype.
Prototype
ConcretePrototype
Client
在以下情况下可以使用 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 }