设计模式——原型模式
原型模式
为了解决类似于从一个对象再创建另外一个可定制的对象,而不需要知道任何创建的细节问题,产生了原型模式。用原型实例指定创建对象的种类,并通过拷贝这些原型创建新的对象。
namespace 原型模式{ class Program { static void Main(string[] args) { ConcretePrototype1 p1 = new ConcretePrototype1("I"); //克隆类ConcretePrototype1的对象p1就能得到新的实例c1 ConcretePrototype1 c1 = (ConcretePrototype1)p1.Clone(); Console.WriteLine("Cloned:{0}", c1.Id); Console.Read(); } } //原型类 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(); } } }
对于.Net而言,那个原型抽象类Prototype是用不着的,因为克隆是在是太常用了,所以.Net在System命名空间中提供了ICloneable接口,其中就是唯一的一个方法Clone(),这样只需要实现这个接口就可以完成原型模式了。
例如,复制一份简历,简历的原型实现
实现
//简历类 class Resume:ICloneable { private string name; private string sex; private string age; private string timeArea; private string company; public Resume(string name) { this.name = name; } //设置个人信息 public void SetPersonalInfo(string sex, string age) { this.sex = sex; this.age = age; } //设置工作经历 public void SetWorkExperience(string timeArea, string company) { this.timeArea = timeArea; this.company = company; } //显示 public void Display() { Console.WriteLine("{0}{1}{2}", name, sex, age); //实现接口的方法,用来克隆对象 Console.WriteLine("工作经历:{0}{1}", timeArea, company); } public object Clone() { return (Object)this.MemberwiseClone(); } }
我们先来看一个例子,简历复制。
结构图工作经历类
//工作经历 class WorkExperience { private string workDate; public string WorkDate { get { return workDate; } set { workDate = value; } } private string company; public string Company { get { return company; } set { company = value; } } }
//简历类 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(); } //设置个人信息 public void SetPersonalInfo(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() { return (Object)this.MemberwiseClone(); } }
class Program { static void Main(string[] args) { Resume a = new Resume("大鸟"); a.SetPersonalInfo("男", "29"); a.SetWorkExperience("1998-2000","XX公司"); Resume b = (Resume)a.Clone(); b.SetWorkExperience("1998-2006", "YY企业"); Resume c = (Resume)a.Clone(); c.SetPersonalInfo("男", "24"); c.SetWorkExperience("1998-2003","ZZ企业"); //b和c都克隆于a,但当它们都设置了“工作经历”时,我们希望的结果是三个的显示不一样 a.Display(); b.Display(); c.Display(); Console.Read(); } }
三个版本信息引用的都是最后一次的设置 ,这是因为,只复制引用,而不复制引用的对象,克隆的过程中并没有创建对象,现在三个引用都指向了同一个工作经历对象。
这就是浅复制,被复制的对象的所有变量都含有与原来的对象相同的值,但所有的对其他对象的引用都仍然指向原来的对象。
被复制对象的所有变量都含有与原来的对象相同的值,除去那些引用其他对象的变量。那些引用其他对象的变量将指向被复制过得新对象,而不再是原有的那些被引用的对象。也就是说把要复制的对象所引用的对象都复制了一遍。
还是以简历复制为例。
结构图简历类
//简历 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(); } //提供Clone方法调用的私有构造函数,以便克隆“工作经历”的数据 //与浅复制的不同之处 private Resume(WorkExperience work) { this.work = (WorkExperience)work.Clone(); } //设置个人信息 public void SetPersonalInfo(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 WorkExperience: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(); } }
运行结果不同,a,b,c引用的对象都是不同的,复制时就一变二,二变三。
深复制把引用对象的变量指向复制过得新对象,而不是原有的被引用的对象。
由于在特定的场合,会经常涉及深复制或浅复制,比如说,数据集对象DataSet,它就有Clone()方法和Copy()方法,Clone()方法用来复制DataSet结构,但不复制DataSet的数据,实现了原型模式的浅复制。Copy()方法不但复制结构,也复制数据,其实就是实现了原型模式的深复制。