这节讲一下原型模式,原型模式用于解决大量相同或相似对象的创建问题,传统的,我们实例化对象要用new关键字,在面对大量重复对象的创建情况下,new实例的过程是比较消耗资源的,所以我们可以利用一个对象作为原型,通过这个对象的不断克隆自己来产出一个个新实例(这跟js的原型对象并不相同,读者不要跟其做理论比较)。
我们可以设想一下,为何克隆比new实例要高效:拿画画来说,new实例相当于每次要构思这幅画,并将其画出来,克隆相当于第一次构思并画出后,后续的画都是临摹,所以克隆是高效的。
我们看一下原型模式的定义:用一个已经创建的实例作为原型,通过复制该原型对象来创建一个和原型相同或相似的新对象。在这里,原型实例指定了要创建的对象的种类。用这种方式创建对象非常高效,根本无须知道对象创建的细节。
在示例代码之前,先要理解一个概念:深拷贝和浅拷贝:
所谓浅拷贝就是指拷贝时只拷贝值类型的属性,引用类型的属性地址跟原型对象的地址指向一致,而深拷贝就是将原型对象的值类型和引用类型的属性都拷贝一份,可以说是和原型对象完全分离开来。
接下来我们看一下如何让一个对象可被克隆:
class prototypeClass : ICloneable
{
public prototypeClass(string classname)
{
this.classname = classname;
}
public object Clone()
{
return MemberwiseClone();
}
public string classname { get; set; }
public void ShowName()
{
Console.WriteLine(classname);
}
}
具体的逻辑就是让原型类实现ICloneable接口,实现接口的Clone()方法,方法中调用MemberwiseClone()返回一个基于该原型对象浅拷贝的对象。
在主方法中调用,我们查看一下运行结果:
prototypeClass p = new prototypeClass("小明");
prototypeClass p_clone = (prototypeClass)p.Clone();
p.ShowName();
p_clone.ShowName();
Console.WriteLine(p == p_clone);
下面我再添加一个类:
在prototypeClass类中添加这个类作为属性:
在主方法中比较一下克隆对象的s属性是否与原型对象的s属性相等:
运行结果如下:
结果为true,这就证明了此为浅拷贝。
若要实现深拷贝,需做点改动:
static prototypeClassS ps=new prototypeClassS("小艾");
public object Clone()
{
prototypeClass p=(prototypeClass) MemberwiseClone();
p.s=(prototypeClassS)ps.Clone();//手动调用属性的克隆方法实现深度拷贝
return p;
}
加入一个静态原型属性,每次克隆该原型对象时都去手动将引用对象克隆出来并赋值,如果被拷贝的属性自己也有引用类型的属性,为了实现深拷贝,也得做对应的修改,这样就形成了一个拷贝链,层层深入,从而完成深度拷贝(其实这挺繁琐的)。
下面我通过代码来演示一下new实例和克隆的性能差距:
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
for (int i = 0; i < 100000; i++)
{
prototypeClass clone = (prototypeClass)p.Clone();
}
stopwatch.Stop();
Console.WriteLine("克隆时间:" + stopwatch.Elapsed);
stopwatch.Start();
for (int i = 0; i < 100000; i++)
{
prototypeClass clone = new prototypeClass("小明",new prototypeClassS("新二"));
}
stopwatch.Stop();
Console.WriteLine("new 实例:" + stopwatch.Elapsed);
在主方法中编写上述代码,下面看一下运行结果:
差距不言而喻。
当然,原型模式也有缺点:
本节到此结束...