Prototype Pattern

一、 原型(Prototype)模式

原型模式的用意是:通过给出一个原型对象来指明所要创建的对象类型,然后用复制这个原型对象的办法创建出更多的同类型对象。

从孙大圣的手段谈起

孙悟空在与黄风怪的战斗中,"使一个身外身的手段:把毫毛揪下一把,用口嚼得粉碎,望上一喷,叫声'变',变有百十个行者,都是一样得打扮,各执一根铁棒,把那怪围在空中。"换而言之,孙悟空可以根据自己的形象,复制出很多"身外身"来。

老孙这种身外身的手段在面向对象设计领域里叫原型(Prototype)模式。

C#对原型模式的支持

在C#里面,我们可以很容易的通过Clone()方法实现原型模式。任何类,只要想支持克隆,必须实现C#中的ICloneable接口。 ICloneable接口中有一Clone方法,可以在类中复写实现自定义的克隆方法。克隆的实现方法有两种:浅拷贝(shallow copy)与深拷贝(deep copy)。

(以下摘自:《.NET框架程序设计(修订版)》,李建忠译)浅拷贝是指当对象的字段值被拷贝时,字段引用的对象不会被拷贝。例如,如果一个对象有 一个指向字符串的字段,并且我们对该对象做了一个浅拷贝,那么两个对象将引用同一个字符串。而深拷贝是对对象实例中字段引用的对象也进行拷贝的一种方式, 所以如果一个对象有一个指向字符串的字段,并且我们对该对象做了一个深拷贝的话,我们将创建一个新的对象和一个新的字符串--新对象将引用新字符串。需要 注意的是执行深拷贝后,原来的对象和新创建的对象不会共享任何东西;改变一个对象对另外一个对象没有任何影响。


二、 Prototype模式的结构:

 Pic55.gif

客户(Client)角色:客户类提出创建对象的请求。
抽象原型(Prototype)角色:这是一个抽象角色,通常由一个C#接口或抽象类实现。此角色给出所有的具体原型类所需的接口。在C#中,抽象原型角色通常实现了ICloneable接口。
具体原型(Concrete Prototype)角色:被复制的对象。此角色需要实现抽象原型角色所要求的接口。


三、 程序举例:

下面的程序给出了一个示意性的实现:

None.gif //  Prototype pattern -- Structural example  
None.gif
using  System;
None.gif
None.gif
//  "Prototype"
None.gif
abstract   class  Prototype
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {
InBlock.gif  
// Fields
InBlock.gif
  private string id;
InBlock.gif
InBlock.gif  
// Constructors
InBlock.gif
  public Prototype( string id )
ExpandedSubBlockStart.gifContractedSubBlock.gif  
dot.gif{
InBlock.gif    
this.id = id;
ExpandedSubBlockEnd.gif  }

InBlock.gif
InBlock.gif  
public string Id
ExpandedSubBlockStart.gifContractedSubBlock.gif  
dot.gif{
ExpandedSubBlockStart.gifContractedSubBlock.gif    
getdot.gifreturn id; }
ExpandedSubBlockEnd.gif  }

InBlock.gif
InBlock.gif  
// Methods
InBlock.gif
  abstract public Prototype Clone();
ExpandedBlockEnd.gif}

None.gif
None.gif
//  "ConcretePrototype1"
None.gif
class  ConcretePrototype1 : Prototype
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {
InBlock.gif  
// Constructors
ExpandedSubBlockStart.gifContractedSubBlock.gif
  public ConcretePrototype1( string id ) : base ( id ) dot.gif{}
InBlock.gif
InBlock.gif  
// Methods
InBlock.gif
  override public Prototype Clone()
ExpandedSubBlockStart.gifContractedSubBlock.gif  
dot.gif{
InBlock.gif    
// Shallow copy
InBlock.gif
    return (Prototype)this.MemberwiseClone();
ExpandedSubBlockEnd.gif  }

ExpandedBlockEnd.gif}

None.gif
None.gif
//  "ConcretePrototype2"
None.gif
class  ConcretePrototype2 : Prototype
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {
InBlock.gif  
// Constructors
ExpandedSubBlockStart.gifContractedSubBlock.gif
  public ConcretePrototype2( string id ) : base ( id ) dot.gif{}
InBlock.gif
InBlock.gif  
// Methods
InBlock.gif
  override public Prototype Clone()
ExpandedSubBlockStart.gifContractedSubBlock.gif  
dot.gif{
InBlock.gif    
// Shallow copy
InBlock.gif
    return (Prototype)this.MemberwiseClone();
ExpandedSubBlockEnd.gif  }

ExpandedBlockEnd.gif}

None.gif
ExpandedBlockStart.gifContractedBlock.gif
/**/ /// <summary>
InBlock.gif
/// Client test
ExpandedBlockEnd.gif
/// </summary>

None.gif class  Client
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {
InBlock.gif  
public static void Main( string[] args )
ExpandedSubBlockStart.gifContractedSubBlock.gif  
dot.gif{
InBlock.gif    
// Create two instances and clone each
InBlock.gif
    ConcretePrototype1 p1 = new ConcretePrototype1( "I" );
InBlock.gif    ConcretePrototype1 c1 
= (ConcretePrototype1)p1.Clone();
InBlock.gif    Console.WriteLine( 
"Cloned: {0}", c1.Id );
InBlock.gif
InBlock.gif    ConcretePrototype2 p2 
= new ConcretePrototype2( "II" );
InBlock.gif    ConcretePrototype2 c2 
= (ConcretePrototype2)p2.Clone();
InBlock.gif    Console.WriteLine( 
"Cloned: {0}", c2.Id );
ExpandedSubBlockEnd.gif  }

ExpandedBlockEnd.gif}

这个例子实现了一个浅拷贝。其中MemberwiseClone()方法是Object类的一个受保护方法,实现了对象的浅拷贝。如果希望实现一个深拷贝,应该实现ICloneable接口,并自己编写ICloneable的Clone接口方法。


四、 带Prototype Manager的原型模式

原型模式的第二种形式是带原型管理器的原型模式,其UML图如下:

 Pic56.gif

客户(Client)角色:客户端类向原型管理器提出创建对象的请求。
抽象原型(Prototype)角色:这是一个抽象角色,通常由一个C#接口或抽象类实现。此角色给出所有的具体原型类所需的接口。在C#中,抽象原型角色通常实现了ICloneable接口。
具体原型(Concrete Prototype)角色:被复制的对象。此角色需要实现抽象的原型角色所要求的接口。
原型管理器(Prototype Manager)角色:创建具体原型类的对象,并记录每一个被创建的对象。


下面这个例子演示了在原型管理器中存储用户预先定义的颜色原型,客户通过原型管理器克隆颜色对象。

None.gif //  Prototype pattern -- Real World example  
None.gif
using  System;
None.gif
using  System.Collections;
None.gif
None.gif
//  "Prototype"
None.gif
abstract   class  ColorPrototype
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {
InBlock.gif  
// Methods
InBlock.gif
  public abstract ColorPrototype Clone();
ExpandedBlockEnd.gif}

None.gif
None.gif
//  "ConcretePrototype"
None.gif
class  Color : ColorPrototype
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {
InBlock.gif  
// Fields
InBlock.gif
  private int red, green, blue;
InBlock.gif
InBlock.gif  
// Constructors
InBlock.gif
  public Color( int red, int green, int blue)
ExpandedSubBlockStart.gifContractedSubBlock.gif  
dot.gif{
InBlock.gif    
this.red = red;
InBlock.gif    
this.green = green;
InBlock.gif    
this.blue = blue;
ExpandedSubBlockEnd.gif  }

InBlock.gif
InBlock.gif  
// Methods
InBlock.gif
  public override ColorPrototype Clone()
ExpandedSubBlockStart.gifContractedSubBlock.gif  
dot.gif{
InBlock.gif    
// Creates a 'shallow copy'
InBlock.gif
    return (ColorPrototype) this.MemberwiseClone();
ExpandedSubBlockEnd.gif  }

InBlock.gif
InBlock.gif  
public void Display()
ExpandedSubBlockStart.gifContractedSubBlock.gif  
dot.gif{
InBlock.gif    Console.WriteLine( 
"RGB values are: {0},{1},{2}",
InBlock.gif      red, green, blue );
ExpandedSubBlockEnd.gif  }

ExpandedBlockEnd.gif}

None.gif
None.gif
//  Prototype manager
None.gif
class  ColorManager
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {
InBlock.gif  
// Fields
InBlock.gif
  Hashtable colors = new Hashtable();
InBlock.gif
InBlock.gif  
// Indexers
InBlock.gif
  public ColorPrototype thisstring name ]
ExpandedSubBlockStart.gifContractedSubBlock.gif  
dot.gif{
ExpandedSubBlockStart.gifContractedSubBlock.gif    
getdot.gifreturn (ColorPrototype)colors[ name ]; }
ExpandedSubBlockStart.gifContractedSubBlock.gif    
setdot.gif{ colors.Add( name, value ); }
ExpandedSubBlockEnd.gif  }

ExpandedBlockEnd.gif}

None.gif
ExpandedBlockStart.gifContractedBlock.gif
/**/ /// <summary>
InBlock.gif
///  PrototypeApp test
ExpandedBlockEnd.gif
/// </summary>

None.gif class  PrototypeApp
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {
InBlock.gif  
public static void Main( string[] args )
ExpandedSubBlockStart.gifContractedSubBlock.gif  
dot.gif{
InBlock.gif    ColorManager colormanager 
= new ColorManager();
InBlock.gif
InBlock.gif    
// Initialize with standard colors
InBlock.gif
    colormanager[ "red" ] = new Color( 25500 );
InBlock.gif    colormanager[ 
"green" ] = new Color( 02550 );
InBlock.gif    colormanager[ 
"blue" ] = new Color( 00255 );
InBlock.gif
InBlock.gif    
// User adds personalized colors
InBlock.gif
    colormanager[ "angry" ] = new Color( 255540 );
InBlock.gif    colormanager[ 
"peace" ] = new Color( 128211128 );
InBlock.gif    colormanager[ 
"flame" ] = new Color( 2113420 );
InBlock.gif
InBlock.gif    
// User uses selected colors
InBlock.gif
    string colorName = "red";
InBlock.gif    Color c1 
= (Color)colormanager[ colorName ].Clone();
InBlock.gif    c1.Display();
InBlock.gif
InBlock.gif    colorName 
= "peace";
InBlock.gif    Color c2 
= (Color)colormanager[ colorName ].Clone();
InBlock.gif    c2.Display();
InBlock.gif
InBlock.gif    colorName 
= "flame";
InBlock.gif    Color c3 
= (Color)colormanager[ colorName ].Clone();
InBlock.gif    c3.Display();
ExpandedSubBlockEnd.gif  }

ExpandedBlockEnd.gif}



五、 浅拷贝与深拷贝

下面给出浅拷贝与深拷贝的两个例子,例子使用了ICloneable接口。C#中的数组是引用型的变量,我们通过数组来进行演示:

浅拷贝:

None.gif using  System;
None.gif
None.gif
class  ShallowCopy : ICloneable
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {
ExpandedSubBlockStart.gifContractedSubBlock.gif  
public int[] v = dot.gif{1,2,3};
InBlock.gif
InBlock.gif  
public Object Clone()
ExpandedSubBlockStart.gifContractedSubBlock.gif  
dot.gif{
InBlock.gif    
return this.MemberwiseClone();
ExpandedSubBlockEnd.gif  }

InBlock.gif
InBlock.gif  
public void Display()
ExpandedSubBlockStart.gifContractedSubBlock.gif  
dot.gif{
InBlock.gif    
foreach(int i in v)
InBlock.gif      Console.Write( i 
+ "");
InBlock.gif    Console.WriteLine();
ExpandedSubBlockEnd.gif  }

ExpandedBlockEnd.gif}

None.gif
None.gif
class  Client
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {
InBlock.gif  
public static void Main()
ExpandedSubBlockStart.gifContractedSubBlock.gif  
dot.gif{
InBlock.gif    ShallowCopy sc1 
= new ShallowCopy();
InBlock.gif    ShallowCopy sc2 
= (ShallowCopy)sc1.Clone();
InBlock.gif    sc1.v[
0= 9;
InBlock.gif
InBlock.gif    sc1.Display();
InBlock.gif    sc2.Display();
ExpandedSubBlockEnd.gif  }

ExpandedBlockEnd.gif}


ShallowCopy对象实现了一个浅拷贝,因此当对sc1进行克隆时,其字段v并没有克隆,这导致sc1与sc2的字段v都指向了同一个v,因此,当修改了sc1的v[0]后,sc2的v[0]也发生了变化。

深拷贝:

None.gif using  System;
None.gif
None.gif
class  DeepCopy : ICloneable
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {
ExpandedSubBlockStart.gifContractedSubBlock.gif  
public int[] v = dot.gif{1,2,3};
InBlock.gif
InBlock.gif  
// 默认构造函数
InBlock.gif
  public DeepCopy()
ExpandedSubBlockStart.gifContractedSubBlock.gif  
dot.gif{
ExpandedSubBlockEnd.gif  }

InBlock.gif
InBlock.gif  
// 供Clone方法调用的私有构造函数
InBlock.gif
  private DeepCopy(int[] v)
ExpandedSubBlockStart.gifContractedSubBlock.gif  
dot.gif{
InBlock.gif    
this.v = (int[])v.Clone();
ExpandedSubBlockEnd.gif  }

InBlock.gif
InBlock.gif  
public Object Clone()
ExpandedSubBlockStart.gifContractedSubBlock.gif  
dot.gif{
InBlock.gif    
// 构造一个新的DeepCopy对象,构造参数为
InBlock.gif    
// 原有对象中使用的 v 
InBlock.gif
    return new DeepCopy(this.v);
ExpandedSubBlockEnd.gif  }

InBlock.gif
InBlock.gif  
public void Display()
ExpandedSubBlockStart.gifContractedSubBlock.gif  
dot.gif{
InBlock.gif    
foreach(int i in v)
InBlock.gif      Console.Write( i 
+ "");
InBlock.gif    Console.WriteLine();
ExpandedSubBlockEnd.gif  }

ExpandedBlockEnd.gif}

None.gif
None.gif
class  Client
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {
InBlock.gif  
public static void Main()
ExpandedSubBlockStart.gifContractedSubBlock.gif  
dot.gif{
InBlock.gif    DeepCopy dc1 
= new DeepCopy();
InBlock.gif    DeepCopy dc2 
= (DeepCopy)dc1.Clone();
InBlock.gif    dc1.v[
0= 9;
InBlock.gif
InBlock.gif    dc1.Display();
InBlock.gif    dc2.Display();
ExpandedSubBlockEnd.gif  }

ExpandedBlockEnd.gif}


这次在克隆的时候,不但克隆对象本身,连里面的数组字段一并克隆。因此,最终打印出来的dc1与dc2不同。


六、 Prototype模式的优点与缺点

Prototype模式的优点包括

1、Prototype模式允许动态增加或减少产品类。由于创建产品类实例的方法是产批类内部具有的,因此增加新产品对整个结构没有影响。

2、Prototype模式提供了简化的创建结构。工厂方法模式常常需要有一个与产品类等级结构相同的等级结构,而Prototype模式就不需要这样。

3、Portotype模式具有给一个应用软件动态加载新功能的能力。由于Prototype的独立性较高,可以很容易动态加载新功能而不影响老系统。

4、产品类不需要非得有任何事先确定的等级结构,因为Prototype模式适用于任何的等级结构。


Prototype模式的缺点:

Prototype模式的最主要缺点就是每一个类必须配备一个克隆方法。而且这个克隆方法需要对类的功能进行通盘考虑,这对全新的类来说不是很难,但对已有的类进行改造时,不一定是件容易的事。


参考文献:
阎宏,《Java与模式》,电子工业出版社
[美]James W. Cooper,《C#设计模式》,电子工业出版社
[美]Alan Shalloway  James R. Trott,《Design Patterns Explained》,中国电力出版社
[美]Robert C. Martin,《敏捷软件开发-原则、模式与实践》,清华大学出版社
[美]Don Box, Chris Sells,《.NET本质论 第1卷:公共语言运行库》,中国电力出版社

转载于:https://www.cnblogs.com/xiongeee/archive/2007/08/14/855633.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值