原型模式(Prototype)
模式定义
原型模式是一种创建型设计模式,Prototype模式允许一个对象再创建另外一个可定制的对象,根本无需知道任何如何创建的细节,工作原理是:通过将一个原型对象传给那个要发动创建的对象,这个要发动创建的对象通过请求原型对象拷贝它们自己来实施创建。
UML类图
- 抽象原型 定义复制方法clone()方法
- 具体原型 实现抽象原型中的复制方法
this.MemberwiseClone()
- 客户端调用
代码结构
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
除外)执行clone()
(实现复杂) - 通过序列化与反序列化(实现简单)
通过序列化深度复制对象,假设一Person
类对象有一Address
类型属性,Address
为引用类型。
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操作数据库,则可先根据原型创建一对象作为修改前值(如果从数据库中查找两次比较耗时)。