黄梅戏红楼梦:设计模式6:Prototype Pattern (原型模式)

来源:百度文库 编辑:九乡新闻网 时间:2024/07/07 12:13:41

设计模式6:Prototype Pattern (原型模式)

分类: 设计模式 2009-06-25 09:25 90人阅读 评论(0) 收藏 举报

出自于:http://www.cnblogs.com/zhenyulu/articles/39257.html

参考于:http://www.dofactory.com/Patterns/PatternPrototype.aspx

一、 原型(Prototype)模式

Definition:Specify the kind of objects to create using a prototypical instance, and create new objects by copying this prototype.

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

从孙大圣的手段谈起

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

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

C#对原型模式的支持

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

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


二、 Prototype模式的结构:

 

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


三、 程序举例:

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

view plain

  1. using System;  
  2. using System.Collections.Generic;  
  3. using System.Linq;  
  4. using System.Text;  
  5.   
  6. namespace ProtypePattern  
  7. {  
  8.     abstract class Prototype  
  9.     {  
  10.         // Fields  
  11.         private string id;  
  12.   
  13.         // Constructors  
  14.         public Prototype(string id)  
  15.         {  
  16.             this.id = id;  
  17.         }  
  18.   
  19.         public string Id  
  20.         {  
  21.             get { return id; }  
  22.         }  
  23.   
  24.         // Methods  
  25.         abstract public Prototype Clone();  
  26.     }  
  27.   
  28.     // "ConcretePrototype1"  
  29.     class ConcretePrototype1 : Prototype  
  30.     {  
  31.         // Constructors  
  32.         public ConcretePrototype1(string id) : base(id) { }  
  33.   
  34.         // Methods  
  35.         override public Prototype Clone()  
  36.         {  
  37.             // Shallow copy  
  38.             return (Prototype)this.MemberwiseClone();  
  39.         }  
  40.     }  
  41.   
  42.     // "ConcretePrototype2"  
  43.     class ConcretePrototype2 : Prototype  
  44.     {  
  45.         // Constructors  
  46.         public ConcretePrototype2(string id) : base(id) { }  
  47.   
  48.         // Methods  
  49.         override public Prototype Clone()  
  50.         {  
  51.             // Shallow copy  
  52.             return (Prototype)this.MemberwiseClone();  
  53.         }  
  54.     }  
  55.   
  56.     /**/  
  57.     ///   
  58.     /// Client test  
  59.     ///   
  60.     class Client  
  61.     {  
  62.         public static void Main(string[] args)  
  63.         {  
  64.             // Create two instances and clone each  
  65.             ConcretePrototype1 p1 = new ConcretePrototype1("I");  
  66.             ConcretePrototype1 c1 = (ConcretePrototype1)p1.Clone();  
  67.             Console.WriteLine("Cloned: {0}", c1.Id);  
  68.   
  69.             ConcretePrototype2 p2 = new ConcretePrototype2("II");  
  70.             ConcretePrototype2 c2 = (ConcretePrototype2)p2.Clone();  
  71.             Console.WriteLine("Cloned: {0}", c2.Id);  
  72.         }  
  73.     }  
  74. }  

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


四、 带Prototype Manager的原型模式

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

 

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


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

view plain
  1. using System;  
  2. using System.Collections.Generic;  
  3. using System.Linq;  
  4. using System.Text;  
  5.   
  6. namespace ProtypePattern  
  7. {  
  8.     abstract class ColorPrototype  
  9.     {  
  10.         // Methods  
  11.         public abstract ColorPrototype Clone();  
  12.     }  
  13.   
  14.     // "ConcretePrototype"  
  15.     class Color : ColorPrototype  
  16.     {  
  17.         // Fields  
  18.         private int red, green, blue;  
  19.   
  20.         // Constructors  
  21.         public Color(int red, int green, int blue)  
  22.         {  
  23.             this.red = red;  
  24.             this.green = green;  
  25.             this.blue = blue;  
  26.         }  
  27.   
  28.         // Methods  
  29.         public override ColorPrototype Clone()  
  30.         {  
  31.             // Creates a 'shallow copy'  
  32.             return (ColorPrototype)this.MemberwiseClone();  
  33.         }  
  34.   
  35.         public void Display()  
  36.         {  
  37.             Console.WriteLine("RGB values are: {0},{1},{2}",  
  38.               red, green, blue);  
  39.         }  
  40.     }  
  41.   
  42.     // Prototype manager  
  43.     class ColorManager  
  44.     {  
  45.         // Fields  
  46.         Hashtable colors = new Hashtable();  
  47.   
  48.         // Indexers  
  49.         public ColorPrototype this[string name]  
  50.         {  
  51.             get { return (ColorPrototype)colors[name]; }  
  52.             set { colors.Add(name, value); }  
  53.         }  
  54.     }  
  55.   
  56.     /**/  
  57.     ///   
  58.     ///  PrototypeApp test  
  59.     ///   
  60.     class PrototypeApp  
  61.     {  
  62.         public static void Main(string[] args)  
  63.         {  
  64.             ColorManager colormanager = new ColorManager();  
  65.   
  66.             // Initialize with standard colors  
  67.             colormanager["red"] = new Color(255, 0, 0);  
  68.             colormanager["green"] = new Color(0, 255, 0);  
  69.             colormanager["blue"] = new Color(0, 0, 255);  
  70.   
  71.             // User adds personalized colors  
  72.             colormanager["angry"] = new Color(255, 54, 0);  
  73.             colormanager["peace"] = new Color(128, 211, 128);  
  74.             colormanager["flame"] = new Color(211, 34, 20);  
  75.   
  76.             // User uses selected colors  
  77.             string colorName = "red";  
  78.             Color c1 = (Color)colormanager[colorName].Clone();  
  79.             c1.Display();  
  80.   
  81.             colorName = "peace";  
  82.             Color c2 = (Color)colormanager[colorName].Clone();  
  83.             c2.Display();  
  84.   
  85.             colorName = "flame";  
  86.             Color c3 = (Color)colormanager[colorName].Clone();  
  87.             c3.Display();  
  88.         }  
  89.     }  
  90. }  


五、 浅拷贝与深拷贝

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

浅拷贝:

view plain

  1. using System;  
  2. using System.Collections.Generic;  
  3. using System.Linq;  
  4. using System.Text;  
  5.   
  6. namespace ProtypePattern  
  7. {  
  8.     class ShallowCopy : ICloneable  
  9.     {  
  10.         public int[] v = { 1, 2, 3 };  
  11.   
  12.         public Object Clone()  
  13.         {  
  14.             return this.MemberwiseClone();  
  15.         }  
  16.   
  17.         public void Display()  
  18.         {  
  19.             foreach (int i in v)  
  20.                 Console.Write(i + ", ");  
  21.             Console.WriteLine();  
  22.         }  
  23.     }  
  24.   
  25.     class Client  
  26.     {  
  27.         public static void Main()  
  28.         {  
  29.             ShallowCopy sc1 = new ShallowCopy();  
  30.             ShallowCopy sc2 = (ShallowCopy)sc1.Clone();  
  31.             sc1.v[0] = 9;  
  32.   
  33.             sc1.Display();  
  34.             sc2.Display();  
  35.         }  
  36.     }  
  37. }  

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

深拷贝:

view plain

  1. using System;  
  2. using System.Collections.Generic;  
  3. using System.Linq;  
  4. using System.Text;  
  5.   
  6. namespace ProtypePattern  
  7. {  
  8.     class DeepCopy : ICloneable  
  9.     {  
  10.         public int[] v = { 1, 2, 3 };  
  11.   
  12.         // 默认构造函数  
  13.         public DeepCopy()  
  14.         {  
  15.         }  
  16.   
  17.         // 供Clone方法调用的私有构造函数  
  18.         private DeepCopy(int[] v)  
  19.         {  
  20.             this.v = (int[])v.Clone();  
  21.         }  
  22.   
  23.         public Object Clone()  
  24.         {  
  25.             // 构造一个新的DeepCopy对象,构造参数为  
  26.             // 原有对象中使用的 v   
  27.             return new DeepCopy(this.v);  
  28.         }  
  29.   
  30.         public void Display()  
  31.         {  
  32.             foreach (int i in v)  
  33.                 Console.Write(i + ", ");  
  34.             Console.WriteLine();  
  35.         }  
  36.     }  
  37.   
  38.     class Client  
  39.     {  
  40.         public static void Main()  
  41.         {  
  42.             DeepCopy dc1 = new DeepCopy();  
  43.             DeepCopy dc2 = (DeepCopy)dc1.Clone();  
  44.             dc1.v[0] = 9;  
  45.   
  46.             dc1.Display();  
  47.             dc2.Display();  
  48.         }  
  49.     }  
  50. }  

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


六、 Prototype模式的优点与缺点

Prototype模式的优点包括

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

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

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

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


Prototype模式的缺点:

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