我们在软件开发的过程中,常常会用到new字段来创建对象。那么是不是只有这一种办法来创建对象呢?答案显然不是的。然而使用new来创建对象时,适用于任何时候呢?显然答案也是否定的,什么时候就不适用new呢?让我们来看看,当我们创建的一个实例的过程很昂贵或者很复杂,并且需要创建多个这样的类的实例时。如果这时候我们仍然用new操作符去创建这样的类的实例,会导致内存中多分配一个一样的类实例对象,这未免会增加创建类的复杂度和消耗更多的内存空间。那有人会说,采用简单工厂模式来创建这样的系统。伴随着产品类的不断增加,导致子类的数量不断增加,反而增加额系统复杂程度,所以在这里使用共产模式也是来封装类的创建过程也是不合适的。怎么办呢?这里就不得不提到原型模式了。
原型模式可以很好地解决这个问题,因为每个类实例都是相同的,当我们需要多个相同的类实例时,没必要每次都使用new运算符去创建相同的类实例对象,此时我们一般思路就是想——只创建一个类实例对象,如果后面需要更多这样的实例,可以通过对原来对象拷贝一份来完成创建,这样在内存中不需要创建多个相同的类实例,从而减少内存的消耗和达到类实例的复用。 然而这个思路正是原型模式的实现方式。下面就具体介绍下设计模式中的原型设计模式。
原型模式(Prototype Pattern)
用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。简单来说就是从一个对象再创建另外一个可定制的对象,而且不需要知道任何的创建细节。在现实生活中,也有很多原型设计模式的例子,例如,细胞分裂的过程,一个细胞的有丝分裂产生两个相同的细胞;还有西游记中孙悟空变出后孙的本领和火影忍者中鸣人的隐分身忍术等。
下面是原型模式的类图,我们一起来看看!
通过上图我们不难发现,在原型模式中只有两个角色:
具体实现代码如下所示:
原型类:
///
/// 抽象类原型
///
public abstract class Prototype
{
private string id;
public string Id
{
get { return id; }
}
public Prototype(string id)
{
this.id = id;
}
///
/// 抽象类的关键就是这个clone()方法
///
///
public abstract Prototype Clone();
}
具体原型类
///
/// 具体原型
///
class ConcretePrototype1 : Prototype
{
public ConcretePrototype1(string id) : base(id)
{
}
///
/// 浅复制
///
///
public override Prototype Clone()
{
//创建当前对象的浅副本
return (Prototype)this.MemberwiseClone();
}
}
(Prototype)this.MemberwiseClone()创建当前对象的浅副本。方法是创建一个新对象,然后将当前对象的非静态字段复制到该新对象。
因此,原始对象及其副本引用同一对象MSDN。
测试:
ConcretePrototype1 cp1 = new ConcretePrototype1("cp1...");
//克隆类ConcretePrototype1的对象cp1就能得到新的实例c1
ConcretePrototype1 c1 = (ConcretePrototype1)cp1.Clone();
Console.WriteLine("Cloned:{0}", c1.Id);
运行结果:
Cloned:cp1...
由于克隆类十分常用,以至于.Net在Syste命名空间中提供了ICloneable接口,其中就只有一个方法Clone(),我们在使用中只需实现这个接口就可以完成原型模式了。下面我们来举例说明:
下面是详细代码:
///
/// 简历类
///
public class Resume : ICloneable
{
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.age = age;
this.sex = sex;
}
///
/// 设置工作经历
///
///
///
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);
}
public object Clone()
{
return (Object)this.MemberwiseClone();
}
}
测试:
Resume p1 = new Resume("大鸟");
p1.SetPersonalInfo("男", "28");
p1.SetWorkExperience("1998-2000", "XX公司");
Resume p2 = (Resume)p1.Clone();
p2.SetWorkExperience("1999-2002", "YY企业");
Resume p3 = (Resume)p1.Clone();
p3.SetPersonalInfo("男", "25");
p1.Display();
p2.Display();
p3.Display();
运行结果:
大鸟,男,28
1998-2000,XX公司
大鸟,男,28
1999-2002,YY企业
大鸟,男,25
1998-2000,XX公司
通过以上代码来创建对象时,不需要每次都new一次,这样大大的提高了性能。一般情况,在初始化的信息不变的情况下,克隆是最好的办法。这既隐藏了对象创建的细节,又对性能做了很大的提升。但是上面的是值类型的克隆,那么对于复杂的引用类型会不会奏效呢?我们把简历中的工作经历改成一个单独的类,代码修改如下:
工作经历类:
///
/// 工作经历
///
public class WorkExperience
{
private string timeArea;
private string company;
public string TimeArea
{
get { return timeArea; }
set { timeArea = value; }
}
public string Company
{
get { return company; }
set { company = value; }
}
}
简历类:
///
/// 简历类
///
public 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.age = age;
this.sex = sex;
}
///
/// 设置工作经历
///
///
///
public void SetWorkExperience(string timeArea, string company)
{
work.TimeArea = timeArea;
work.Company = company;
}
///
/// 显示
///
public void Display()
{
Console.WriteLine("{0},{1},{2}",name,sex,age);
Console.WriteLine("{0},{1}",work.TimeArea,work.Company);
}
public object Clone()
{
return (Object)this.MemberwiseClone();
}
}
测试:
Resume p1 = new Resume("大鸟");
p1.SetPersonalInfo("男", "28");
p1.SetWorkExperience("1998-2000", "XX公司");
Resume p2 = (Resume)p1.Clone();
p2.SetWorkExperience("1999-2002", "YY企业");
Resume p3 = (Resume)p1.Clone();
p3.SetPersonalInfo("男", "25");
p3.SetWorkExperience("1998-2000", "ZZ公司");
p1.Display();
p2.Display();
p3.Display();
运行结果:
大鸟,男,28
1998-2000,ZZ公司
大鸟,男,28
1998-2000,ZZ公司
大鸟,男,25
1998-2000,ZZ公司
这里你是否想到了刚才说的,复制的情况又两种,值类型和引用类型,引用是不同的,可以点击查看MSDN。这其中提到浅复制,上面第一个例子,可以复制成功是浅复制的原因就是都引用的是值类型。而这里的是引用类型,对引用的对象还是指向了原来的对象,即只引用了地址。所以造成了工作经历都是相同的,而且是最后一个。那么什么是深复制和浅复制呢?下文解释。
还是刚才我们改为深复制:
工作经历类:
///
/// 工作经历
///
public class WorkExperience:ICloneable
{
private string timeArea;
private string company;
public string TimeArea
{
get { return timeArea; }
set { timeArea = value; }
}
public string Company
{
get { return company; }
set { company = value; }
}
public object Clone()
{
return (object)this.MemberwiseClone();
}
}
简历类:
///
/// 简历类
///
public class Resume : ICloneable
{
private string name;
private string sex;
private string age;
private WorkExperience work;
private Resume(WorkExperience work)
{
this.work = (WorkExperience)work.Clone();
}
///
/// 为Clone()方法调用的私有构造函数,以便于克隆“工作经历”的数据
///
///
public Resume(string name)
{
this.name = name;
work = new WorkExperience();
}
///
/// 设置个人信息
///
///
///
public void SetPersonalInfo(string sex, string age)
{
this.age = age;
this.sex = sex;
}
///
/// 设置工作经历
///
///
///
public void SetWorkExperience(string timeArea, string company)
{
work.TimeArea = timeArea;
work.Company = company;
}
///
/// 显示
///
public void Display()
{
Console.WriteLine("{0},{1},{2}",name,sex,age);
Console.WriteLine("{0},{1}",work.TimeArea,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;
}
}
测试,我们继续用上面的,运行结果如下:
大鸟,男,28
1998-2000,XX公司
大鸟,男,28
1999-2002,YY企业
大鸟,男,25
1998-2000,ZZ公司
这次修改是不是达到了预期呢?深复制复制了是新的对象,与原来的对象没有共享关系。
优点:
缺点:
原型模式适用于:
也是写几个类,这里就不在演示了!
最美好的时光总是短暂的,原型模式的介绍就结束了,原型模式用一个原型对象来指明所要创建的对象类型,然后用复制这个原型对象的方法来创建出更多的同类型对象。它与工厂方法模式的实现非常相似,其中原型模式中的Clone方法就类似工厂方法模式中的工厂方法,只是工厂方法模式的工厂方法是通过new运算符重新创建一个新的对象(相当于原型模式的深拷贝实现),而原型模式是通过调用MemberwiseClone方法来对原来对象进行拷贝,也就是复制,同时在原型模式优点中也介绍了与工厂方法的区别。好了一句话,深复制创建的对象与原对象没有任何共享,一个对象的改变不会影响到另外一个对象;而浅复制是共享的,一个改变了,另外一个对象的成员的值会随之改变。
The End
好了,今天的分享就到这里,如有不足之处,还望大家及时指正,随时欢迎探讨交流!!!
喜欢的朋友们,请帮顶、点赞、评论!您的肯定是我写作的不竭动力!
相关阅读
C# 23种设计模式(unity演示)