原型模式(Prototype)定义:用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。
例子背景: 找工作需要准备大量的简历,可是我不想一份一份的码字,于是想起了复制粘贴功能。朋友也托我帮他做简历,格式可以全完复制粘贴,要想让简历内容不一样,就需要用到深复制功能,这里用显示不同工作经历举例。
复制功能还有浅复制、深复制的区别。
原型类:.net提供了ICloneable接口,其中就有方法Clone(),所以其实不必写原型类,实现接口即可。
abstract class Prototype { private string id; public Prototype(string id) //构造函数重载 { this.id=id; } public string Id //Id属性只读 { get {return id;} } public abstract Prototype Clone(); //抽象类的Clone方法 }
具体原型类:
//具体原型类实现复制 class ConcretePrototype1:Prototype { public ConcretePrototype1(string id):base(id) {} public override Prototype Clone() //重写Clone方法 { return (Prototype)this.MemberwiseClone(); //MemberwiseClone()就是浅复制 } }
客户端代码:
static void Main (string[] args) { ConcretePrototype1 p1 = new ConcretePrototype1("I"); ConcretePrototype1 c1 = (ConcretePrototype1)p1.Clone(); //把Clone后的值强转成ConcretePrototype1类型 Console.WriteLine("Clone:{0}",c1.Id); Console.Read(); }
浅复制: 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, sex, 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(); //这里用到浅复制方法了 }
客户端:
static void Main(string[] args) { Resume a = new Resume("飞机"); a.SetPersonalInfo("男","29"); a.SetWorkExperience("2020年11月29日","九天星月公司"); Resume b = (Resume)a.Clone(); //a的值浅复制给了b b.SetWorkExperience("2020年11月29日","米西我的超人公司"); //引用被覆盖 Resume c = (Resume)a.Clone(); //a的值浅复制给了c c.SetPersonalInfo("男","24"); //重新复制 c.SetWorkExperience("2020年11月30日","瓜里瓜气公司"); //引用被覆盖 a.Display(); b.Display(); c.Display(); Console.Read(); }
显示:显示的工作经历都是最后一次的值
飞机 男 29 工作经历:2020年11月30日 瓜里瓜气公司 飞机 男 29 工作经历:2020年11月30日 瓜里瓜气公司 飞机 男 24 工作经历:2020年11月30日 瓜里瓜气公司
深复制: 把引用的对象的变量指向复制过的新对象,而不是原有的引用对象。
工作经历类:
class WorkExperience:ICloneable //实现了接口 { 省略…… } public Object Clone() //实现克隆方法 { return (object)this.MemberwiseClone(); //依然是MemberwiseClone(),但简历类里传值方式会有改变 }
简历类:
class Resume:ICloneable //实现接口 { private string name, sex, 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 SetPersonalInfo(string sex,string age) { 不变,省略…… } //设置工作经历 public void SetWorkExperience(string workDate, string company) { 不变,省略…… } //显示 public void Display() { 不变,省略…… } public Object Clone() { //使用构造方法,让工作经历浅复制完成,赋值给新对象,而不是原来被引用的对象 Resume obj = new Resume(this.work); obj.name = this.name; obj.sex = this.name; obj.age = this.age; return obj; }
客户端代码不变。
显示:这次三次工作经历都不相同。
飞机 男 29 工作经历:2020年11月29日 九天星月公司 飞机 大鸟 29 工作经历:2020年11月29日 米西我的超人公司 飞机 男 24 工作经历:2020年11月30日 瓜里瓜气公司