模式定义
原型模式是一种创建型设计模式,Prototype模式允许一个对象再创建另外一个可定制的对象,根本无需知道任何如何创建的细节,工作原理是:通过将一个原型对象传给那个要发动创建的对象,这个要发动创建的对象通过请求原型对象拷贝它们自己来实施创建。
UML类图
- 抽象原型
- 具体原型
客户端调用
代码结构
MemberwiseClone()
方法是Object
对象的浅复制的方法。public static class Client { public static void Run() { ConcretePrototype p = new ConcretePrototype("I"); ConcretePrototype c = (ConcretePrototype)p.Clone(); if(p != c) { Console.WriteLine("The Copy Object is not same."); } Console.WriteLine(c.Id); } } public abstract class Prototype { private string _id; public Prototype(string id) { this._id = id; } public string Id { get { return _id; } } public abstract Prototype Clone(); } public class ConcretePrototype : Prototype { public ConcretePrototype(string id) : base(id) { } public override Prototype Clone() { return (Prototype)this.MemberwiseClone(); } }
C#代码优化
其实没必要定义抽象原型对象,如那个类需要具有复制的功能,直接继承
ICloneable
接口就可以了public class ConcretePrototype : ICloneable { private string _id; public string Id { get { return _id; } } public ConcretePrototype(string id) { this._id = id; } public object Clone() { return this.MemberwiseClone(); } }
深度复制
以上谈论的类中不包含引用类型成员(string
类型除外,因每次操作其实新建一个对象,可当作值类型处理)。如果包含引用成员,以上为浅复制(即引用成员被对象和复制对象公用)。当然这是不希望的两种解决办法。
- 通过递归反射,发放存在引用成员(
string
除外)新建对象(实现复杂) 通过序列化与反序列化(实现简单)
通过序列化深度复制对象,假设一人对象有名称和住址,住址为引用类型。public static class PrototypeClient { public static void Run() { Person p1 = new Person() { Name = "LoveTomato", Address = new Address("China", "BeiJing", "Haidian") }; Person p2 = p1.Clone() as Person; p2.Address.Area = "Chaoyang"; Console.Write("\nName:{0},Address{1}", p1.Name, p1.Address.ToString()); Console.Write("\nName:{0},Address{1}", p2.Name, p2.Address.ToString()); } } [Serializable] public class Person : ICloneable { public string Name { get; set; } public Address Address { get; set; } public object Clone() { BinaryFormatter bf = new BinaryFormatter(); MemoryStream ms = new MemoryStream(); bf.Serialize(ms, this); ms.Position = 0; return (bf.Deserialize(ms)); } } [Serializable] public class Address { public string Country { get; set; } public string City { get; set; } public string Area { get; set; } public Address(string country, string city, string area) { this.Country = country; this.City = city; this.Area = area; } public override string ToString() { return string.Format("Country:{0},City:{1},Area:{2}", this.Country, this.City, this.Area); } }
情景模式
在新建一个对象花费代价比较大时(需要从数据库或远程获取数据等),可以使用原型法创建对象。
在对数据库中数据修改做日志时,要求记录修改前值与修改后值。因为项目通过ORM操作数据库,则可先根据原型创建一对象作为修改前值(如果从数据库中查找两次比较耗时)。