要求有一个简历,必须要有姓名,可以设置性别和年龄,可以设置工作经历,最终需要写三份简历。
初步实现
简历类:
// 简历类
class Resume {
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);
}
}
客户端调用代码:
static void Main(string[] args){
Resume a= new Resume("大鸟");
a.SetPersonalInfo("男","29");
a.SetWorkExperience("1998-2000","XX公司");
Resume b= new Resume("大鸟");
b.SetPersonalInfo("男","29");
b.SetWorkExperience("1998-2000","XX公司");
Resume c= new Resume("大鸟");
c.SetPersonalInfo("男","29");
c.SetWorkExperience("1998-2000","XX公司");
a.Display();
b.Display();
c.Display();
Console.Read();
}
结果显示:
考虑:三份简历需要三次实例化,这样的客户端是不是很麻烦?如果要二十份,就需要二十次实例化。不仅如此,如果写错了一个字,比如98年改成99年,那就要改二十次!
其实,可以这样写——
static void Main(string[] args){
Resume a= new Resume("大鸟");
a.SetPersonalInfo("男","29");
a.SetWorkExperience("1998-2000","XX公司");
Resume b=a;
Resume c=a;
a.Display();
b.Display();
c.Display();
Console.Read();
}
这其实是传引用,而不是传值。这样做就如同是在b纸张和c纸张上写着简历在a处一样,没有实际内容。
原型模式
原型模式(Prototype),用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。
原型模式其实就是从一个对象再创建另外一个可定制的对象,而且不需知道任何创建的细节。
来看代码——
原型类:
abstract class Prototype{
private string id;
public Prototype(string id){
this.id=id;
}
public string Id{
get{return id;}
}
// 抽象类关键就是有这样一个Clone方法
public abstract Prototype Clone();
}
具体原型类:
class ConcretePrototype1 : Prototype{
public ConcretePrototype1(string id) :base(id){}
public override Prototype Clone(){
// 创建当前对象的浅表副本,方法是创建一个新对象,
// 然后将当前对象的非静态字段复制到该新对象,
// 如果字段是值类型的,则对该字段执行逐位复制;
// 如果字段是引用类型,则复制引用但不复制引用的对象
// 因此,原始对象及其副本引用同一对象
return (Prototype)this.MeberwiseClone();
}
}
客户端代码:
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();
}
.NET在System命名空间中提供了ICloneable接口,其中就是唯一的一个方法Clone(),这样就只需要实现这个接口就可以完成原型模式了。
第二版
代码结构图:
简历类:
// 简历
class Resume : ICloneable{
private string name;
private string sex;
private string age;
private string timeArea;
private 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}",timaArea,company); // 实现接口的方法,用来克隆对象
}
public Object Clone(){
return (Object)this.MemberwiseClone();
}
}
客户端调用代码:
static void Main(string[] args){
Resume a= new Resume("大鸟");
a.SetPersonalInfo("男","29");
a.SetWorkExperience("1998-2000","XX公司");
// 只需要调用Clone方法就可以实现新简历的生成,并且可以再修改新简历的细节
Resume b= (Resume)a.Clone();
b.SetWorkExperience("1998-2006","YY企业");
Resume c =(Resume)a.Clone();
c.SetPersonalInfo("男","24");
a.Display();
b.Display();
c.Display();
Console.Read();
}
结果显示:
一般在初始化的信息不发生变化的情况下,克隆是最好的办法。这既隐藏了对象创建的细节,又对性能大大的提高。克隆等于是不用重新初始化对象,而是动态地获得对象运行时的状态。
浅复制与深复制
现在简历对象里的数据都是string型的,而string是一种拥有值类型特点的特殊引用类型。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.MemberqiseClone();
}
}
客户端调用代码:
static void Main(string[] args){
Resume a = new Resume("大鸟");
a.SetPersonalInfo("男","29");
a.SetWorkExperience("1998-2000","XX公司");
// b和c都克隆于a,但当它们都设置了“工作经历时”,
// 我们希望的结果是三个的显示不一样
Resume b= (Resume)a.Clone();
b.SetWorkExperience("1998-2006","YY企业");
Resume c = (Resume)a.Clone();
c.SetWorkExperience("1998-2003","ZZ公司");
a.Display();
b.Display();
c.Display();
Console.Read();
}
结果显示:
由于MemberwiseClone是浅复制,被复制对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用都仍然指向原来的对象。但我们可能更需要这样的一种需求,把要复制的对象所引用的对象都复制一遍,比如刚才的例子,我们希望是a、b、c三个引用的对象不同的复制时就一变二,二变三。此时,我们就叫这种方式为“深复制”,深复制把引用对象的变量指向复制过的新对象,而不是原有的被引用的对象。
注意多层引用比较复杂,不要出现循环引用的问题。
最终版-简历的深复制实现
代码结构图:
工作经历类:
// 工作经历
// 让“工作经历”实现ICloneable接口
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();
}
}
简历类:
// 简历
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;
}
}
同之前的客户端代码,其结果显示:
本章完。
------------------------------------------------------------------------------------------------------------------------------------------------------------------------- 本文是连载文章,此为第七章,学习从一个对象创建另外一个可定制的对象,而且不需知道任何创建的细节的原型模式。
上一章:https://blog.youkuaiyun.com/qq_36770641/article/details/82801288 工厂方法模式
下一章:
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------