原型(Prototype)模式的用意是:通过给出一个原型对象来指明索要创建的对象类型,然后用复制这个原型对象的方法创建出更多的同类型对象。
不同语言对原型模式的支持
一、Java
Java语言中已经提供了clone方法,定义在Object类中,需要实现克隆功能的类,只需要实现java.lang.Cloneable接口即可。
二、C#
- 在C#里面,我们可以很容易的通过Clone()方法实现原型模式。任何类,只要想支持克隆,必须实现C#中的ICloneable接口。
ICloneable接口中有一Clone方法,可以在类中腹泻,实现自定义的克隆方法。 - 克隆的实现方法有两种:浅复制(shallow copy)和深复制(deep copy)
- 浅复制:只负责克隆按值传递的数据(比如基本数据类型和String类型)
- 深复制:除了浅复制要克隆的值外,还负责引用雷子那个(属性的类型也是对象)的数据。
- 需要注意的是:执行拷贝后,原来的对象和新创建的对象不会共享任何东子,改变一个对象和另一个对象没有任何影响。
Prototype模式的结构
- 客户角色(Client)角色:
客户类提出创建对象的请求。 - 抽象原型(Prototype)角色:
这是一个抽象角色,通常由一个C#接口或抽象类实现,此角色给出所有的具体原型类所需的接口。在C#中,抽象原型角色同创实现了ICloneable接口。 - 具体原型(Concrete Prototype)角色
被复制的对象。此角色需要实现抽象原型角色索要求的接口。
MemberwiseClone方法创建一个浅表副本,方法是创建一个新对象,然后将当前对象的非静态字段复制到该新都巷。如果字段是值类型的,则对该字段执行逐位复制。如果字段是引用类型,则复制引用但不复制引用的对象;因此,原始对象及其复本引用同一对象。
优点
- 想要改某份简历,只需要对这一份简历作一定的修改就可以,不会影响到其他简历,相同的部分就不用再重复。
- 一般在初始化的信息不发生变化的情况下,克隆是最好的方法,即隐藏了创建的细节,又对性能是大大的提高。
复制
- 浅复制:被复制的对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用都仍然指向原来的对象。
- 深复制:吧引用对象的变量指向复制过的新对象,而不是原有的被引用的对象。
Prototype模型本质
- 克隆生成对象
- 如果要生成一大批很相像的类的实例时,不用每次去做重复的复制工作。
eg:如果有一个类A,打算生成100个A的实例,并且这些实例的变量值大部分相同,只有一小部分不一样。使用原型只需要生成一个A的实例,在通过clone来生成其他的实例,然后一一修改其他实例不同的地方。
Prototype模型与Factory模式的关系
原型模式使用起来完全可以达到工厂模式的效果;而且用起来甚至比工厂模式更方便、灵活。
对于工厂模式与原型模式在功能上的这点巧合,也许是因为本来工厂模式和原型模式都是创建型模式(三种类型,行为,结构)。他们的基本功能都能生成对象,因为是的原型模式在功能上可以替代工厂模式。
Prototype模式如何实现Factory模式的功能
- 工厂模式实现的生产产品的功能,关键是利用了继承的特性。也就是说,你生成的产品,一定是由同一个抽象产品类派生出来的。所以,在工厂模式下,你如果要生成一类产品,就要引入一个抽像产品类,然后再由它派生出具体产品。
- 同样,在原型模式中,你完全可以同样定义一个这样的“抽象产品——具体产品”层次,再利用具体产品本身的clone功能来产生具体产品本身。从而达到实现工厂模式功能的目的。
- 实际上,在原型模式中,每个具体产品就扮演了工厂模式里的具体工厂的角色(因为每个具体产品都具有生成自己拷贝的功能,从这种意义上讲,这正是工厂的作用)
总结
- Prototype模式通过复制原型(Prototype)而获得新对象创建的功能,这里Prototype本身就是“对象工厂”(因为能够生产对象)
- Prototype模式从自身复制自己创建新对象
一、优点
- Prototype模式允许动态增加或减少产品类。由于创建产品类实例的方法是产品类内部具有的,因此增加新产品对整个结构没有影响。
- Prototype模式提供了简化的创建结构。工厂方法模式常常需要有一个与产品类等级结构相同的等级结构,而Prototype模式就不需要这样
二、缺点
Prototype模式的最主要的缺点就是每个原型的子类都必须实现clone的操作,尤其在包含引用类型饿随想时,clone方法会比较麻烦,必须要能够递归地让所有的相关对象都要正确地实现克隆。
实例
【问题】订单处理系统
现在有个个订单处理系统,里面有一个保存订单的业务功能,需我:每当订单的预定产品数量超过1000的时候,就需要把订单拆成两份订单来保存。如果拆成两份后还是超过1000,则继续拆分,直到每份产品预订数量不超过1000.
根据业务,目前的订单系统分成两种,一种是个人订单、一种是公司订单。
客户名称、产品对象(ID, Name),订购产品数量。
公司名称、产品对象(ID, Name),订购产品数量。
【代码】
using System;
namespace OrderPrototype
{
class Product
{
private string ID, name;
public Product() { }
public Product(string ID,string name)
{
this.ID = ID;
this.name = name;
}
public string id
{
get { return ID; }
set { ID = value; }
}
public string Name
{
get { return name; }
set { name = value; }
}
public Object Clone()
{
return (Object)this.MemberwiseClone();
}
}
abstract class Iorder
{
abstract public Iorder Clone();
}
class Order:Iorder
{
private string name;
private int num;
private Product pro;
public Order(string name)
{
this.name = name;
this.pro = new Product();
}
public int Num
{
get { return num; }
set { num = value; }
}
private Order(Product pro)
{
this.pro=(Product)pro.Clone();
}
public void setProduct(string name,string ID)
{
pro.Name = name;
pro.id = ID;
}
public override Iorder Clone()
{
Order obj = new Order(this.pro);
obj.name = this.name;
obj.num = this.num;
return obj;
}
public void Display()
{
Console.WriteLine("订单持有人/公司:"+name+" 订单数量:"+num);
Console.WriteLine("订单编号:" + pro.id + " 订单产品:" + pro.Name);
}
}
class Client
{
static void Main(string[] args)
{
Order person = new Order("小菜");
person.Num=12300;
person.setProduct("《设计模式》", "123456789");
Order []orders=new Order[10010];
int n = person.Num / 1000;
if (person.Num>1000)
{
for (int i=1 ; i<=n ; i++)
{
orders[i] = (Order)person.Clone();
orders[i].Num = 1000;
}
if (person.Num % 1000 != 0)
{
orders[n + 1] = (Order)person.Clone();
orders[n + 1].Num = person.Num % 1000;
n++;
}
}
for(int i=1;i<=n;i++)
{
orders[i].Display();
}
}
}
}
【UMLt图】