一,、 原型(Prototype)模式
原型模式的用意是:通過給出一個原型對象來指明所要創(chuàng)建的對象類型,,然后用復制這個原型對象的辦法創(chuàng)建出更多的同類型對象,。
從孫大圣的手段談起
孫悟空在與黃風怪的戰(zhàn)斗中,,"使一個身外身的手段:把毫毛揪下一把,用口嚼得粉碎,,望上一噴,,叫聲‘變‘,變有百十個行者,,都是一樣得打扮,,各執(zhí)一根鐵棒,把那怪圍在空中,。"換而言之,,孫悟空可以根據自己的形象,復制出很多"身外身"來,。
老孫這種身外身的手段在面向對象設計領域里叫原型(Prototype)模式,。
C#對原型模式的支持
在C#里面,我們可以很容易的通過Clone()方法實現(xiàn)原型模式,。任何類,,只要想支持克隆,必須實現(xiàn)C#中的ICloneable接口,。
ICloneable接口中有一Clone方法,,可以在類中復寫實現(xiàn)自定義的克隆方法,。克隆的實現(xiàn)方法有兩種:淺拷貝(shallow
copy)與深拷貝(deep copy),。
(以下摘自:《.NET框架程序設計(修訂版)》,,李建忠譯)淺拷貝是指當對象的字段值被拷貝時,字段引用的對象不會被拷貝,。例如,,如果一個對象有
一個指向字符串的字段,并且我們對該對象做了一個淺拷貝,,那么兩個對象將引用同一個字符串,。而深拷貝是對對象實例中字段引用的對象也進行拷貝的一種方式,
所以如果一個對象有一個指向字符串的字段,,并且我們對該對象做了一個深拷貝的話,,我們將創(chuàng)建一個新的對象和一個新的字符串--新對象將引用新字符串。需要
注意的是執(zhí)行深拷貝后,,原來的對象和新創(chuàng)建的對象不會共享任何東西,;改變一個對象對另外一個對象沒有任何影響。
二,、 Prototype模式的結構:
客戶(Client)角色:客戶類提出創(chuàng)建對象的請求,。
抽象原型(Prototype)角色:這是一個抽象角色,通常由一個C#接口或抽象類實現(xiàn),。此角色給出所有的具體原型類所需的接口,。在C#中,抽象原型角色通常實現(xiàn)了ICloneable接口,。
具體原型(Concrete Prototype)角色:被復制的對象,。此角色需要實現(xiàn)抽象原型角色所要求的接口。
三,、 程序舉例:
下面的程序給出了一個示意性的實現(xiàn):
// 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();
}
}
/**////
/// Client test
/// 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 );
}
}
這個例子實現(xiàn)了一個淺拷貝,。其中MemberwiseClone()方法是Object類的一個受保護方法,實現(xiàn)了對象的淺拷貝,。如果希望實現(xiàn)一個深拷貝,,應該實現(xiàn)ICloneable接口,并自己編寫ICloneable的Clone接口方法,。
四,、 帶Prototype Manager的原型模式
原型模式的第二種形式是帶原型管理器的原型模式,其UML圖如下:
客戶(Client)角色:客戶端類向原型管理器提出創(chuàng)建對象的請求,。
抽象原型(Prototype)角色:這是一個抽象角色,,通常由一個C#接口或抽象類實現(xiàn)。此角色給出所有的具體原型類所需的接口。在C#中,,抽象原型角色通常實現(xiàn)了ICloneable接口,。
具體原型(Concrete Prototype)角色:被復制的對象。此角色需要實現(xiàn)抽象的原型角色所要求的接口,。
原型管理器(Prototype Manager)角色:創(chuàng)建具體原型類的對象,,并記錄每一個被創(chuàng)建的對象。
下面這個例子演示了在原型管理器中存儲用戶預先定義的顏色原型,,客戶通過原型管理器克隆顏色對象,。
// 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 ); }
}
}
/**////
/// PrototypeApp test
/// 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#中的數(shù)組是引用型的變量,我們通過數(shù)組來進行演示:
淺拷貝:
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對象實現(xiàn)了一個淺拷貝,,因此當對sc1進行克隆時,,其字段v并沒有克隆,這導致sc1與sc2的字段v都指向了同一個v,,因此,,當修改了sc1的v[0]后,sc2的v[0]也發(fā)生了變化,。
深拷貝:
using System;
class DeepCopy : ICloneable
{
public int[] v = {1,2,3};
// 默認構造函數(shù)
public DeepCopy()
{
}
// 供Clone方法調用的私有構造函數(shù)
private DeepCopy(int[] v)
{
this.v = (int[])v.Clone();
}
public Object Clone()
{
// 構造一個新的DeepCopy對象,,構造參數(shù)為
// 原有對象中使用的 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();
}
}
這次在克隆的時候,不但克隆對象本身,,連里面的數(shù)組字段一并克隆,。因此,最終打印出來的dc1與dc2不同,。
六、 Prototype模式的優(yōu)點與缺點
Prototype模式的優(yōu)點包括
1,、Prototype模式允許動態(tài)增加或減少產品類,。由于創(chuàng)建產品類實例的方法是產批類內部具有的,因此增加新產品對整個結構沒有影響,。
2,、Prototype模式提供了簡化的創(chuàng)建結構。工廠方法模式常常需要有一個與產品類等級結構相同的等級結構,,而Prototype模式就不需要這樣,。
3、Portotype模式具有給一個應用軟件動態(tài)加載新功能的能力,。由于Prototype的獨立性較高,,可以很容易動態(tài)加載新功能而不影響老系統(tǒng)。
4、產品類不需要非得有任何事先確定的等級結構,,因為Prototype模式適用于任何的等級結構,。
Prototype模式的缺點:
Prototype模式的最主要缺點就是每一個類必須配備一個克隆方法。而且這個克隆方法需要對類的功能進行通盤考慮,,這對全新的類來說不是很難,,但對已有的類進行改造時,不一定是件容易的事,。