原型模式(Prototype),用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。
简单说来原型模式就是从一个对象再创建另外一个可定制的对象,而且不需知道任何创建的细节。
原型模式UML图:
原型模式的基本代码结构:
namespace ConsoleApplication1 { abstract class Prototype { private string id; public Prototype(string id) { this.id = id; } public string Id { get { return id; } } public abstract Prototype Clone(); //抽象类的关键就是要有这样一个克隆方法 } //具体原型类 class ConcretePrototype1 : Prototype { public ConcretePrototype1(string id): base(id) { } public override Prototype Clone() { return (Prototype)this.MemberwiseClone(); //创建当前对象的浅表副本。方法是创建一个新对象,然后将当前对象的非静态字段复制到该新对象。 //如果字段是值类型,则对该字段执行逐位复制,如果字段是引用类型,则复制引用但不复制引用的对象 //因此,原始对象及其副本引用同意对象 } } class Program { static void Main(string[] args) { ConcretePrototype1 p1 = new ConcretePrototype1("I"); ConcretePrototype1 c1 = (ConcretePrototype1)p1.Clone(); //原型模式创建对象 Console.WriteLine("Cloned:{0}",c1.Id); //输出 Cloned:I Console.ReadKey(); } } }
原型模式的作用:我们知道每new一次对象,都需要执行一次构造函数,如果构造函数的执行时间很长,那么多次的执行初始化操作就比较低效。一半在初始化的信息不发生变化的情况下,克隆是最好的办法,这既隐藏了对象创建的细节,又能对性能是大大的提高。不用重新初始化对象,而是动态地获得对象运行时的状态。
在使用原型模式中,有必要注意浅复制与深复制的概念。
对于上面的原型结构示例代码:我们知道如果字段是值类型的,则对该字段执行逐位复制,如果字段是引用类型的,则复制引用但不复制引用的对象;因此,原始对象及其副本引用同意对象。
浅复制:被复制对象的所有变量都含有与原来对象相同的值,而所有的对其他对象的引用都仍然指向原来的对象。也就是说,浅复制复制出来的对象,如果里面含有引用类型的数据,则浅复制复制出来的对象与原对象是同一个,因为仅复制引用地址嘛。
深复制:当执行复制时,被复制的对象含有引用类型对象的变量时,引用类型的变量对象也重新复制一份,即复制出来的引用对象与原类中的对象不是指向同一个地址。
因此,在使用原型模式时必须要分析好,是该浅复制还是深复制。
回到《大话设计模式中的示例》 简历的原型实现:
namespace ConsoleApplication1 { class WorkExperience : ICloneable //主要,要让工作经历实现ICloneable { private string workDate; public string WorkDate { get { return workDate; } set { workDate = value; } } private string company; public string Company { get { return company; } set { company = value; } } public Object Clone() { return (Object)this.MemberwiseClone(); } } //简历类 class Resume : ICloneable { private string name; private string sex; private string age; private WorkExperience work; public Resume(string Name) { this.name = Name; work = new WorkExperience(); } private Resume(WorkExperience Work) { this.work = (WorkExperience)Work.Clone(); //注意深复制必须要添加此段代码,提供Clone方法调用的私有构造函数以便克制工作经历 } //设置个人信息 public void SetPersonnalInfo(string Sex, string Age) { this.sex = Sex; this.age = Age; } //设置工作经历 public void SetWorkExperience(string workDate, string company) { work.WorkDate = workDate; work.Company = company; } //显示 public void Display() { Console.WriteLine("{0} {1} {2}",name,sex,age); Console.WriteLine("工作经历:{0} {1}", work.WorkDate, work.Company); } public Object Clone() { Resume obj = new Resume(this.work); obj.name = this.name; obj.sex = this.sex; obj.age = this.age; return obj; } } class Program { static void Main(string[] args) { Resume a = new Resume("大鸟"); a.SetPersonnalInfo("男","29"); a.SetWorkExperience("1998-2000","微软"); Resume b = (Resume)a.Clone(); b.SetWorkExperience("2000-2004","google"); Resume c = (Resume)a.Clone(); c.SetPersonnalInfo("男","24"); c.SetWorkExperience("1998-2004","苹果"); a.Display(); b.Display(); c.Display(); Console.ReadKey(); } } }