用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象
原型模式其实就是从一个对象再创建另外一个可定制的对象,而且不需要知道任何创建的细节,基本代码如下:
//原型类
abstractclass Prototype
{
private string id;
public Prototype(string id)
{
this.id = id;
}
public string Id()
{
get{ retrun id; }
}
//抽象类关键就是有这样一个Clone方法
public abstract Prototype Clone();
};
//具体原型类
classConcretPrototype1 :Prototype
{
public ConcretPrototype1(string id) :base(id)
{
}
public override Prototype Clone()
{
//https://msdn.microsoft.com/zh-cn/library/system.object.memberwiseclone.aspx
//MemberwiseClone方法创建一个浅表副本,方法是创建一个新对象,然后将当前对象的非静态字段复制到该新对象。
//如果字段是值类型的,则对该字段执行逐位复制。 如果字段是引用类型,则复制引用但不复制引用的对象;因此,原始对象及其复本引用同一对象。
return (Prototype)this.MemberwiseClone();
}
};
//客户端代码
voidmain()
{
ConcretPrototype1 p1 = new ConcretPrototype1("I");
ConcretPrototype1 c1 = (ConcretPrototype1)p1.Clone(); //克隆类 ConcretPrototype1 的对象p1就能得到新的实例c1
}
其实,原型抽象类Prototype是用不着的。
因为,克隆实在是太常用了,.Net提供了ICloneable接口,其中唯一的方法就是Clone(), 这样只要实现这个接口就可以完成原型模式了
下面给出一个实例:
原题是需要给出几份相同的简历,但是我觉得有些牵强,于是就把题目改为生成几额类似的矩形,可以有细节上的差异。(虽然我把原题的代码结构图贴上来了)
classRectangle :ICloneable
{
private int length;
private int width;
private string color;
private string birthtime;
private string author;
public Rectangle(string author)
{
this.author = author;
}
//detail infomation
public void SetTangleInfo(int length, intwidth, string color)
{
this.length = length;
this.width = width;
this.color = color;
}
//set create time
public void SetTime(string birthtime)
{
this.birthtime = birthtime;
}
//show
public void Display()
{
Console.WriteLine("{0}{1} {2}", length, width, color);
Console.WriteLine("CreateTime : {0}, Author : {1} ", birthtime, author);
}
public Object Clone
{
return (Object)this.MemberwiseClone();
}
};
//客户端
voidmain()
{
Rectangle a = new Rectangle("Ben");
a.SetTangleInfo(10,20,"red");
a.SetTime("201504091049");
//只需要调用Clone方法就可以实现新矩形的生成,并且可以修改新矩形的细节
Rectangle b = (Rectangle)a.Clone();
b.SetTime("201504091050");
Rectangle c = (Rectangle)a.Clone();
c.SetTangleInfo(20,10,"blue");
a.Display();
b.Display();
c.Display();
}
这样一来,如果每个细节都一样,那么我们的代码可以显得十分清爽。
性能也得到了提高,比如看对象a的初始化,假设是复杂的项目,如果每个对象都这样,那么就太低效了。
一般在初始化信息不发生变化的情况下,克隆是最好的办法。这既隐藏了对象创建的细节,又对性能是大大的提高。
相当于是不用重新初始化对象,而是动态的获得对象运行时的状态。
但是,我们也发现,我们使用的MemberwiseClone() 函数,根据描述是浅拷贝,这里再把那段话拿过来看看:
【https://msdn.microsoft.com/zh-cn/library/system.object.memberwiseclone.aspx
MemberwiseClone方法创建一个浅表副本,方法是创建一个新对象,然后将当前对象的非静态字段复制到该新对象。
如果字段是值类型的,则对该字段执行逐位复制。 如果字段是引用类型,则复制引用但不复制引用的对象;因此,原始对象及其复本引用同一对象。】
当前矩形类中的数据都是值类型(int,string),所以没有什么问题,但是如果类中有对象引用,那么引用的对象数据是不会被克隆过来的。
接下来我们修改一下矩形类,让其中的时间类型不再是string类型而是一个类。那么在使用的时候就需要注意了。
下面就给出实现了深克隆的代码: (关于仅仅使用浅克隆的结果想必很容易想到,所以就不贴上来了)
不过这里我依旧把简历的类结构图贴上来:
classBirthTime :ICloneable
{
private int year;
public int Year
{
get{ return year; }
set{ year = value; }
}
private int month;
public int Month
{
get{ return month; }
set{ month = value; }
}
private int day;
public int Day
{
get{ return day; }
set{ day = value; }
}
public Object Clone() //时间类实现Clone方法
{
return (Object)this.MemberwiseClone();
}
};
classRectangle :ICloneable
{
private int length;
private int width;
private string color;
private BirthTime bt;
private string author;
public Rectangle(string author)
{
this.author = author;
bt = new BirthTime();
}
public Rectangle(BirthTime bt)
{ //这个构造函数是为了将来深克隆用的
this.bt = (BirthTime)bt.Clone(); //这里我们虽然是浅克隆,但是我们这里也只需要浅克隆就行了,因为该类里全是值类型而没有引用的
}
//detail infomation
public void SetTangleInfo(int length, intwidth, string color)
{
this.length = length;
this.width = width;
this.color = color;
}
//set create time
public void SetTime(int year, int month,int day)
{
bt.Year = year;
bt.Month = month;
bt.Day = day;
}
//show
public void Display()
{
Console.WriteLine("{0}{1} {2}", length, width, color);
Console.WriteLine("CreateTime : {0} {1} {2}, Author : {3} ", bt.Year, bt.Month, bt.Day, author);
}
public Object Clone()
{
Rectangle obj = new Rectangle(this.bt); //这里就用到了之前准备好的构造函数,能这么用也依赖于BirthTime类中没有引用且实现的浅克隆(如果BirthTime中有引用,那么就需要一层一层的浅克隆来实现接下来一层的深克隆了)
obj.length = this.length;
obj.width = this.width;
obj.color = this.color;
obj.author = this.author;
return obj; //最终返回一个深复制的矩形对象
}
};
//客户端
voidmain()
{
Rectangle a = new Rectangle("Ben");
a.SetTangleInfo(10, 20, "red");
a.SetTime(2015,4,9);
//只需要调用Clone方法就可以实现新矩形的生成,并且可以修改新矩形的细节
Rectangle b = (Rectangle)a.Clone();
b.SetTime(2015,4,10);
Rectangle c = (Rectangle)a.Clone();
c.SetTangleInfo(20, 10, "blue");
a.Display();
b.Display();
c.Display();
}
这样,我们的输出结果就不再像浅克隆一样,a和b的时间类指向同一个时间对象,导致输出相同的时间了。