您的位置:首页 > 其它

设计模式之三 原型模式(Prototype)

2012-12-24 10:55 393 查看

概要

原型模式的主要思想是基于现有的对象克隆一个新的对象出来,一般是有对象的内部提供克隆的方法,通过该方法返回一个对象的副本,这种创建对象的方式,相比我们之前说的几类创建型模式还是有区别的,之前的讲述的工厂模式与抽象工厂都是通过工厂封装具体的new操作的过程,返回一个新的对象,有的时候我们通过这样的创建工厂创建对象是不值得的。

目的

使用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。

使用条件

1、如果说我们的对象类型不是刚开始就能确定,而是这个类型是在运行期确定的话,那么我们通过这个类型的对象克隆出一个新的类型更容易。

2、有的时候我们可能在实际的项目中需要一个对象在某个状态下的副本,这个前提很重要,这点怎么理解呢,例如有的时候我们需要对比一个对象经过处理后的状态和处理前的状态是否发生过改变,可能我们就需要在执行某段处理之前,克隆这个对象此时状态的副本,然后等执行后的状态进行相应的对比,这样的应用在项目中也是经常会出现的。

3、当我们在处理一些对象比较简单,并且对象之间的区别很小,可能只是很固定的几个属性不同的时候,可能我们使用原型模式更合适

结构图



原型模式代码

class Program
{
static void Main(string[] args)
{
ConcretePrototype1 p1 = new ConcretePrototype1("I");
ConcretePrototype1 c1 = (ConcretePrototype1)p1.Clone();
//克隆类ConcretePrototype1的对象P1就能得到新的实例c1
Console.WriteLine("Cloned: {0}", c1.Id);

ConcretePrototype2 p2 = new ConcretePrototype2("II");
ConcretePrototype2 c2 = (ConcretePrototype2)p2.Clone();
Console.WriteLine("Cloned: {0}", c2.Id);

// Wait for user
Console.Read();

}
}

abstract class Prototype
{
private string id;

// Constructor
public Prototype(string id)
{
this.id = id;
}

// Property
public string Id
{
get { return id; }
}
//抽象类关键就是有这样的一个Clone存在
public abstract Prototype Clone();
}

class ConcretePrototype1 : Prototype
{
// Constructor
public ConcretePrototype1(string id)
: base(id)
{
}

public override Prototype Clone()
{
// Shallow copy
//创建当前对象的浅表副本,方法是创建一个新对象,然后将当前对象的非静态字段复制到该新对象,如果字段
//是值类型的则该字段是逐行诸位复制,如果是引用类型的,则复制引用但不复制引用的对象,即原始对象及其
//副本引用同一对象
return (Prototype)this.MemberwiseClone();
}
}

class ConcretePrototype2 : Prototype
{
// Constructor
public ConcretePrototype2(string id)
: base(id)
{
}

public override Prototype Clone()
{
// Shallow copy
return (Prototype)this.MemberwiseClone();
}
}

运行后结果



注意在.NET来说,那个原型抽象类Prototype是用不到的,因为克隆实在是很常用的,.NET在system命名空间中提供了IChoneable接口,其中就是唯一的一个方法Clone(),这样只需要去实现这个接口就可以完成原型模式。

下面我们进一步的深入学习

深复制与浅复制

我们学习原型模式的具体应用,我们就先来学习认识深复制与前复制,否则我们不能够清晰的知道原型模式的具体的克隆过程和克隆出来的对象的相信情况:

深复制后的对象和对象副本



深复制后的对象和对象副本



浅复制是在副本中重新创建的成员,对应到内存的栈上,分配新的内存空间,那么对于引用类型则因为浅复制的时候,对象和对象副本共同使用一个引用对象,那么不管是在对象还是对象副本中修改了相应的引用成员之后,这个引用类型的成员就会发生变化。

因为2个对象指向同一个内存地址,那么任何一个修改操作都会产生改变。

浅复制与深复制实例

简历浅复制实例

class Program
{
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.SetPersonalInfo("男", "24");
c.SetWorkExperience("1998-2003", "ZZ企业");

a.Display();
b.Display();
c.Display();

Console.Read();
}
}

//简历
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.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();
}

private Resume(WorkExperience work)
{//提供Clone方法调用的私有构造函数,以便克隆工作经历的数据
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;
}

}

//工作经历
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();
}
}


运行后结果:



总结

原型模式作为创建型模式中的最特殊的一个模式,具体的创建过程,是由对象本身提供,这样我们在很多的场景下,我们可以很方便的快速的构建新的对象,就像前面分析讲解的几类场景中,可能我们通过使用对象的克隆,比通过其他几类的创建型模式,效果要好的多,而且代价也小很多。打个比方,原型模式对于系统的扩展,可以做到无缝的扩展,为什么这么说呢?比如其他的创建型工厂,如果新增一个对象类型,那么我们不管是修改配置文件的方式,还是修改代码的形式,无疑我们都是需要进行修改的,对于我们大家通用的公共应用来说这无疑是危险的,那么通过原型模式,则可以解决这样的问题,因为类型本身实现这样的方法即可,但是也有一定的缺点,每个对象都实现这样的方法,无疑是很大的工作量,但是在某些特殊的环境下,或者实际的项目中,可能原型模式是好的选择。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: