一、 原型(Prototype)模式

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

从孙大圣的手段谈起

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

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

C#对原型模式的支持

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

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

二、 Prototype模式的结构:

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

三、 程序举例:

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

// Prototype pattern -- Structural example  
using System;

// "Prototype"
abstract class Prototype
{
  // Fields
  private string id;

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

  public string Id
  {
    get{ return id; }
  }

  // Methods
  abstract public Prototype Clone();
}

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

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

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

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

/**//// <summary>
/// Client test
/// </summary>
class Client
{
  public static void Main( string[] args )
  {
    // Create two instances and clone each
    ConcretePrototype1 p1 = new ConcretePrototype1( "I" );
    ConcretePrototype1 c1 = (ConcretePrototype1)p1.Clone();
    Console.WriteLine( "Cloned: {0}", c1.Id );

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

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

四、 带Prototype Manager的原型模式

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

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

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

// Prototype pattern -- Real World example  
using System;
using System.Collections;

// "Prototype"
abstract class ColorPrototype
{
  // Methods
  public abstract ColorPrototype Clone();
}

// "ConcretePrototype"
class Color : ColorPrototype
{
  // Fields
  private int red, green, blue;

  // Constructors
  public Color( int red, int green, int blue)
  {
    this.red = red;
    this.green = green;
    this.blue = blue;
  }

  // Methods
  public override ColorPrototype Clone()
  {
    // Creates a 'shallow copy'
    return (ColorPrototype) this.MemberwiseClone();
  }

  public void Display()
  {
    Console.WriteLine( "RGB values are: {0},{1},{2}",
      red, green, blue );
  }
}

// Prototype manager
class ColorManager
{
  // Fields
  Hashtable colors = new Hashtable();

  // Indexers
  public ColorPrototype this[ string name ]
  {
    get{ return (ColorPrototype)colors[ name ]; }
    set{ colors.Add( name, value ); }
  }
}

/**//// <summary>
///  PrototypeApp test
/// </summary>
class PrototypeApp
{
  public static void Main( string[] args )
  {
    ColorManager colormanager = new ColorManager();

    // Initialize with standard colors
    colormanager[ "red" ] = new Color( 255, 0, 0 );
    colormanager[ "green" ] = new Color( 0, 255, 0 );
    colormanager[ "blue" ] = new Color( 0, 0, 255 );

    // User adds personalized colors
    colormanager[ "angry" ] = new Color( 255, 54, 0 );
    colormanager[ "peace" ] = new Color( 128, 211, 128 );
    colormanager[ "flame" ] = new Color( 211, 34, 20 );

    // User uses selected colors
    string colorName = "red";
    Color c1 = (Color)colormanager[ colorName ].Clone();
    c1.Display();

    colorName = "peace";
    Color c2 = (Color)colormanager[ colorName ].Clone();
    c2.Display();

    colorName = "flame";
    Color c3 = (Color)colormanager[ colorName ].Clone();
    c3.Display();
  }
}

五、 浅拷贝与深拷贝

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

浅拷贝:

using System;

class ShallowCopy : ICloneable
{
  public int[] v = {1,2,3};

  public Object Clone()
  {
    return this.MemberwiseClone();
  }

  public void Display()
  {
    foreach(int i in v)
      Console.Write( i + ", ");
    Console.WriteLine();
  }
}

class Client
{
  public static void Main()
  {
    ShallowCopy sc1 = new ShallowCopy();
    ShallowCopy sc2 = (ShallowCopy)sc1.Clone();
    sc1.v[0] = 9;

    sc1.Display();
    sc2.Display();
  }
}

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

深拷贝:

using System;

class DeepCopy : ICloneable
{
  public int[] v = {1,2,3};

  // 默认构造函数
  public DeepCopy()
  {
  }

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

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

  public void Display()
  {
    foreach(int i in v)
      Console.Write( i + ", ");
    Console.WriteLine();
  }
}

class Client
{
  public static void Main()
  {
    DeepCopy dc1 = new DeepCopy();
    DeepCopy dc2 = (DeepCopy)dc1.Clone();
    dc1.v[0] = 9;

    dc1.Display();
    dc2.Display();
  }
}

这次在克隆的时候,不但克隆对象本身,连里面的数组字段一并克隆。因此,最终打印出来的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卷:公共语言运行库》,中国电力出版社

Prototype Pattern相关推荐

  1. 乐在其中设计模式(C#) - 原型模式(Prototype Pattern)

    [索引页] [源码下载] 乐在其中设计模式(C#) - 原型模式(Prototype Pattern) 作者:webabcd 介绍 用原型实例指定创建对象的种类,并且通过拷贝这个原型来创建新的对象. ...

  2. 极速理解设计模式系列:4.原型模式(Prototype Pattern)

    四个角色:抽象原型角色(Prototype).具体原型角色(ConcretePrototype).原型管理器角色(PrototypeManager).客户端角色(Client) 抽象原型角色(Prot ...

  3. 五、原型模式(Prototype Pattern)

    1. 什么是原型模式(Prototype Pattern) 在需要创建大量相同或者相似的对象时,先创建一个原型对象,然后利用这个原型对象进行克隆,从而得到大量对象 java中可以利用Object中的c ...

  4. 7.Java设计模式-----原型模式(Prototype Pattern)

    什么是原型模式? 原型模式(Prototype Pattern)是用于创建重复的对象,同时又能保证性能.这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式. 这种模式是实现了一个原型接口 ...

  5. C++设计模式——原型模式(Prototype Pattern)

    C++设计模式--原型模式(Prototype Pattern) 微信公众号:幼儿园的学霸 目录 文章目录 C++设计模式--原型模式(Prototype Pattern) 目录 定义 代码示例 普通 ...

  6. 原型模式(Prototype Pattern)

    原型模式(Prototype Pattern) 原型模式(Prototype Pattern)是用于创建重复的对象,同时又能保证性能.这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式. ...

  7. 设计模式---原型模式(Prototype Pattern)

    在编程中有时候我们会发现,当我们需要一个实例,可是这个实例的创建过程十分复杂,在执行过程中 会消耗大量的时间,同时创建第一个实例和创建第二个时间的初始化信息并未改变.在此种情况下,直接New 一个实例 ...

  8. Prototype Pattern(原型模式)

    意图 用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象. 适用性 • 当要实例化的类是在运行时刻指定时,例如,通过动态装载: • 为了避免创建一个与产品类层次平行的工厂类层次时: • 当 ...

  9. 设计模式学习笔记九:原型模式(Prototype Pattern)

    1.概述     意图:我们将已经存在的对象作为原型,用户可以通过复制这些原型创建新的对象.     使用场合:当一个系统应该独立于产品的创建.构造和表示时,可以使用原型模式.在原型模式中,产品的创建 ...

最新文章

  1. 微信小程序获取用户收货地址 完整代码
  2. UVa12532 - Interval Product(线段树)
  3. 2 s锁是什么_innodb存储引擎读书笔记:锁
  4. php xml写入数据库中,PHP读取xml并写入数据库示例
  5. session共享怎么做的(分布式如何实现session共享)?
  6. 问题之传递参数名和接收参数名要一致。
  7. 第八期:继美商务部拉黑多家中国公司后,MIT开始审查对华AI合作项目
  8. 苹果发布iOS 12.1.4操作系统更新 修复FaceTime安全漏洞
  9. 列表标签(HTML)
  10. WebLogic—在Eclipse上配置WebLogic Server
  11. n个人围成一圈指针c语言,C语言N个人围成一圈报数用二级指针实现,不用数组!...
  12. 中文金融情感词典发布啦 | 附代码
  13. java坦克大战 素材_坦克大战 游戏源码+ 素材+文档(了解面向对象的具体编程)
  14. 关于Linux下ISE和vivado安装cable usb驱动的问题
  15. 2017腾讯暑期实习生从笔试到面试总结(附带华为、阿里面试经历)
  16. 免费开源FTP工具:Cyberduck for Mac
  17. 织梦后台图片预览功能跳到顶部怎么修改
  18. 【大学物理·早期量子论和量子力学基础】量子力学中的氢原子问题
  19. ADO,ADO.NET
  20. ad18差分布线,设置差分对

热门文章

  1. Linux下备份系统
  2. CentOS 7 网页加载速度慢的解决办法
  3. 配置windows失败,不能进入系统
  4. mapreduce.job.reduce.slowstart.completedmaps
  5. PHP根据IP获取当前所在地地址
  6. 20110125 学习记录:在SQL Server 2005数据库中修改存储过程
  7. 先搞清楚了任务究竟是什么再说
  8. 程序员的爱情 第六章
  9. ExtJS实战(10)-项目总结
  10. windows下的cmd命令(全面)