一、简介(Brief Introduction)
用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。
我们都知道,创建型模式一般是用来创建一个新的对象,然后我们使用这个对象完成一些对象的操作,我们通过原型模式可以快速的创建一个对象而不需要提供专门的new()操作就可以快速完成对象的创建,这无疑是一种非常有效的方式,快速的创建一个新的对象。
二、模式分析(Analysis)
客户(Client)角色:使用原型对象的客户程序
抽象原型(Prototype)角色:规定了具体原型对象必须实现的接口(如果要提供深拷贝,则必须具有实现clone的规定)
具体原型(ConcretePrototype):从抽象原型派生而来,是客户程序使用的对象,即被复制的对象。此角色需要实现抽象原型角色所要求的接口。
三、案例分析(Example)
namespace原型模式
{
1、客户端
class Program { static void Main(string[] args) { ConcretePrototype1 p1 = newConcretePrototype1("I"); ConcretePrototype1c1 = (ConcretePrototype1)p1.Clone(); //克隆类ConcretePrototypel的对象p1就得到新的实例c1 Console.WriteLine("Cloned:{0}", c1.Id); Console.Read(); } }
2、抽象原型
abstract class Prototype { private string id; public Prototype(string id) { this.id = id; } public string Id { get { return id; } } public abstractPrototype Clone(); //抽象类有个抽象方法 }
3、具体原型
class ConcretePrototype1 : Prototype { public ConcretePrototype1(string id) :base(id) { } public override Prototype Clone() { return(Prototype)this.MemberwiseClone(); //创建当前对象的副本。方法是创建一个新的对象,然后将当前对象的非静态字段复制到该新对象。如字段为值类型的,则对该字段进行逐位复制。若为引用类型,则复制引用但不复制引用对象; } }}
四、解决的问题(What To Solve)
• 1)当一个系统应该独立于它的产品创建、构成和表示时,要使用 Prototype模式
• 2)当要实例化的类是在运行时刻指定时,例如,通过动态装载;
• 3)为了避免创建一个与产品类层次平行的工厂类层次时
•4)当一个类的实例只能有几个不同状态组合中的一种时。建立相应数目的原型并克隆它们可能比每 次 用合适的状态手工实例化该类更方便一些。(也就是当我们在处理一些对象比较简单,并且对象之间的区别很小,可能只是很固定的几个属性不同的时候,可能我们使用原型模式更合适)。
五、扩展(Extend)
浅拷贝和深拷贝的原理图:
1、浅拷贝
被拷贝对象的所有变量都含有与原对象相同的值,而且对其他对象的引用仍然是指向原来的对象。即浅拷贝只负责当前对象实例,对引用的对象不做拷贝。
浅复制后的对象和对象副本的情况:
namespace 浅复制{ class Program { static void Main(string[] args) { Resume a = newResume("大鸟"); a.SetPersonalInfo("男","29"); a.SetWokeExperience("1998-2000", "XX公司"); Resume b =(Resume)a.Clone(); //b和c都克隆与a b.SetWokeExperience("1998-2000", "YY企业"); Resume c = (Resume)a.Clone(); c.SetPersonalInfo("男","24"); c.SetWokeExperience("1998-2003", "ZZ企业"); a.Display(); b.Display(); c.Display(); Console.Read(); } } //工作经历 class WorkExperience { private string workDate; public string WorkDate { get { return workDate; } set { workDate = value; } } private string company; public string Comapany { get { return company; } set { company = value; } } } //简历类 class Resume : ICloneable { private string name; private string sex; private string age; private WorkExperiencework; //引用工作经历对象 public Resume(stringname) //在“简历”类实例化时同时实例化“工作经历” { this.name = name; work = new WorkExperience(); } //设置个人信息 public void SetPersonalInfo(string sex,string age) { this.sex = sex; this.age = age; } //设置工作经历 public void SetWokeExperience(stringworkDate, string company) { work.WorkDate = workDate; work.Comapany = company; } //显示 public void Display() { Console.WriteLine("{0} {1}{2}", name, sex, age); Console.WriteLine("工作经历:{0} {1}", work.WorkDate,work.Comapany); //显示“工作经历”的两个属性值 } public object Clone() { return(Object)this.MemberwiseClone(); } }}
自己的理解:浅复制是对值类型的逐位赋值
2、深拷贝
被拷贝对象的所有的变量都含有与原来对象相同的值,除了那些引用其他对象的变量。那些引用其他对象的变量将指向一个被拷贝的新对象,而不再是原有那些被引用对象。即 深拷贝把要拷贝的对象所引用的对象也都拷贝了一次, 而这种对被引用到的对象拷贝叫做间接拷贝。
深复制的对象和对象副本的情况:
namespace 简历的深复制实现{ class Program { static void Main(string[] args) { Resume a = newResume("大鸟"); a.SetPersonalInfo("男","29"); a.SetWokeExperience("1998-2000", "XX公司"); Resume b = (Resume)a.Clone(); b.SetWokeExperience("1998-2000", "YY企业"); Resume c = (Resume)a.Clone(); c.SetPersonalInfo("男","24"); c.SetWokeExperience("1998-2003","ZZ企业"); a.Display(); b.Display(); c.Display(); Console.Read(); } } //工作经历 class WorkExperience : ICloneable { private string workDate; public string WorkDate { get { return workDate; } set { workDate = value; } } private string company; public string Comapany { get { return company; } set { company = value; } } public object Clone() “工作经历”类实现克隆方法,Object是所有类的基类 { 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(); } privateResume(WorkExperience work) //提供Clone方法调用的私有构造函数,以便克隆工作经历数据 { this.work =(WorkExperience)work.Clone(); } //设置个人信息 public void SetPersonalInfo(string sex,string age) { this.sex = sex; this.age = age; } //设置工作经历 public void SetWokeExperience (stringworkDate, string company) { work.WorkDate = workDate; work.Comapany = company; } //显示 public void Display() { Console.WriteLine("{0} {1}{2}", name, sex, age); Console.WriteLine("工作经历:{0}{1}", work.WorkDate, work.Comapany); } public object Clone() { Resume obj = newResume(this.work); //调用私有构造方法,让“工作经历”克隆完成,然后再给这个“简历”对象的相关字段赋值,最终返回一个深复制的简历对象。 obj.name = this.name; obj.sex = this.sex; obj.age = this.age; return obj; } } }
三次显示结果不同
自己的理解:简历和工作经历共实现一个接口,先让“工作经历”克隆完成,然后再给“简历”对象相关字段赋值
六、优点(Advantage)
Prototype模式有许多和AbstractFactory模式 和 Builder模式一样的效果:它对客户隐藏了具体的产品类,因此减少了客户知道的名字的数目。此外,这些模式使客户无需改变即可使用与特定应用相关的类。
下面列出Prototype模式的另外一些优点。
1 ) 运行时刻增加和删除产品: Prototype允许只通过客户注册原型实例就可以将一个新的具体产品类并入系统。它比其他创建型模式更为灵活,因为客户可以在运行时刻建立和删除原型。
2 ) 改变值以指定新对象: 高度动态的系统允许你通过对象复合定义新的行为—例如,通过为一个对象变量指定值—并且不定义新的类。你通过实例化已有类并且将这些实例注册为客户对象的 原型,就可以有效定义新类别的对象。客户可以将职责代理给原型,从而表现出新的行为。这种设计使得用户无需编程即可定义新“类” 。实际上,克隆一个原型类似于实例化一个类。Prototype模式可以极大的减少系统所需要的类的数目。
3) 改变结构以指定新对象:许多应用由部件和子部件来创建对象。
4) 减少子类的构造 Factory Method 经常产生一个与产品类层次平行的 Creator类层次。Prototype模式使得你克隆一个原型而不是请求一个工厂方法去产生一个新的对象。因此你根本不需要Creator类层次。这一优点主要适用于像 C + +这样不将类作为一级类对象的语言。像Smalltalk和Objective C这样的语言从中获益较少,因为你总是可以用一个类对象作为生成者。在这些语言中,类对象已经起到原型一样的作用了。
5) 用类动态配置应用 一些运行时刻环境允许你动态将类装载到应用中。在像 C + +这样的语言中,Prototype模式是利用这种功能的关键。一个希望创建动态载入类的实例的应用不能静态引用类的构造器。而应该由运行环境在载入时自 动创建每个类的实例,并用原型管理器来注册这个实例(参见实现一节) 。这样应用就可以向原型管理器请求新装载的类的实例,这些类原本并没有和程序相连接。 E T + +应用框架[ W G M 8 8 ]有一个运行系统就是使用这一方案的。
Prototype的主要缺陷是每一个Prototype的子类都必须实现clone操作,这可能很困难。
例如,当所考虑的类已经存在时就难以新增 clone操作。当内部包括一些不支持拷贝或有循环引用的对象时,实现克隆可能也会很困难的。
七、总结(Summary)
原型模式的主要思想是基于现有的对象克隆一个新的对象出来,一般是有对象的内部提供克隆的方法,通过该方法返回一个对象的副本,这种创建对象的方式,相比我们之前说的几类创建型模式还是有区别的,之前的讲述的工厂模式与抽象工厂都是通过工厂封装具体的new操作的过程,返回一个新的对象,有的时候我们通过这样的创建工厂创建对象不值得。