原型模式(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日 瓜里瓜气公司