### 一、上篇回顧
上篇創建者模式中,我們主要講述了創建者的幾類實現方案,和創建者模式的應用的場景和特點,創建者模式適合創建復雜的對象,并且這些對象的每個組成部分的詳細創建步驟可以是動態的變化的,但是每個對象的組裝的過程來說可能是相對固定的或者說是對象的創建的過程是固定的,那么通過創建者模式可以很好的解決這類復雜對象的創建,而在我們的生活中或者是項目中可能會有這個方面的需求,那么使用創建者模式無疑是好的選擇。
創建者模式中的每個對象組成部分的構建都是對象本身提供的內部方法,具體的創建者只是調用要創建的對象的內部的相應組成部分的構建方法,組織這些對象內部構建方法的執行順序,完成對象的完整構建。當我們的客戶應用程序需要調用這個創建者時,我們只需要通過指導者調用的形式,提供統一的創建者訪問入口,通過構造函數注入或者配置文件的形式來完成創建者的注入。
### 二、摘要
本文主要是講述創建型模式中一個比較特殊的模式-原型模式,這個模式呢,有個最大的特點是克隆一個現有的對象,這個克隆的結果有2種,一種是是淺復制,另一種是深復制,這里我們也會探討下深復制和淺復制的原理,這樣可能更方便大家理解這個原型模式的使用。我們都知道,創建型模式一般是用來創建一個新的對象,然后我們使用這個對象完成一些對象的操作,我們通過原型模式可以快速的創建一個對象而不需要提供專門的new()操作就可以快速完成對象的創建,這無疑是一種非常有效的方式,快速的創建一個新的對象。本文將會從以下幾個方面進行講述:
1、原型模式的使用場景和特點
2、淺復制和深復制的原理。
3、舉例說明淺復制和深復制。
4、原型模式的實現方案。
5、總結原型模式。
我們這里先給出一個原型模式的原理圖:
[](http://images.cnblogs.com/cnblogs_com/hegezhou_hot/WindowsLiveWriter/3711c6dafbd3_12FBE/image_2.png)
### 三、本文大綱
a、上篇回顧。
b、摘要。
c、本文大綱。
d、原型模式的特點及使用場景。
e、深復制和淺復制。
f、原型模式的實現方案。
g、原型模式使用總結。
h、系列進度。
i、下篇預告。
### 四、原型模式的特點及使用場景
原型模式的主要思想是基于現有的對象克隆一個新的對象出來,一般是有對象的內部提供克隆的方法,通過該方法返回一個對象的副本,這種創建對象的方式,相比我們之前說的幾類創建型模式還是有區別的,之前的講述的工廠模式與抽象工廠都是通過工廠封裝具體的new操作的過程,返回一個新的對象,有的時候我們通過這樣的創建工廠創建對象不值得,特別是以下的幾個場景的時候,可能使用原型模式更簡單也效率更高。
1、如果說我們的對象類型不是剛開始就能確定,而是這個類型是在運行期確定的話,那么我們通過這個類型的對象克隆出一個新的類型更容易。這個怎么理解。例如我們有的時候在處理DataTable中的記錄進行篩選后,放在一個新的DataTable 中,我們知道如果說2個dataTable的架構不同,那么必須手動的顯示的賦值,否則無法使用如下方式進行導入數據:
下面給出測試的相關代碼和說明
~~~
public class DataTableDemo?
?? {?
?????? public void CloneTest()?
?????? {?
?????????? string cmdText = "SELECT * FROM TABLE";?
?????????? DataTable dt = new DataTable();?
?????????? //通過執行上面的cmdText 返回一個dataTable對象;
?????????? //這時候我們可以如下形式復制一個新的dataTable,而不用先創建一個dataTable,然后把每一列都顯示的循環添加到新的dataTable中,
?????????? //這是很大的工作量。?
?????????? DataTable dt1 = dt.Clone();?
?????????? //克隆一個新的對象 dt1.
?????????? #region 不采用克隆的形式復制一個新的dataTable
?????????? DataTable dt2 = new DataTable();
?????????? foreach (DataColumn column in dt.Columns)?
?????????? {?
?????????????? dt2.Columns.Add(column.ColumnName);?
?????????? }
?????????? #endregion?
?????? }?
?? }
~~~
2、有的時候我們可能在實際的項目中需要一個對象在某個狀態下的副本,這個前提很重要,這點怎么理解呢,例如有的時候我們需要對比一個對象經過處理后的狀態和處理前的狀態是否發生過改變,可能我們就需要在執行某段處理之前,克隆這個對象此時狀態的副本,然后等執行后的狀態進行相應的對比,這樣的應用在項目中也是經常會出現的。
假設我們有這樣的需求,我們在ORM框架的設計中,經常會遇到這樣的問題,我們在處理某個對象的編輯狀態的時候,我們想框架給我們生成的更新數據庫的SQL語句,不包含數據列沒有發生變化的列,不要出現在更新語句中,這個時候,可能一個方案會是,編輯前克隆一個對象,然后等編輯后提交的時候,生成相應的語句時進行對比之前克隆的對象,看看是否數據發生變化,如果說對象的部分數據列發生變化,那么就只是把變化的數據列進行更新。
當然上面我只是給出了一種比較簡單的,但是效率不是很高的實現方案,還有很多好的方案我就不討論了,這里只是為了說明原型模式的可用場景。
如果對上面的方式不是很理解或者看文字比較累的話,可以看下面的圖,應該就比較清晰了。
[](http://images.cnblogs.com/cnblogs_com/hegezhou_hot/WindowsLiveWriter/3711c6dafbd3_12FBE/image_4.png)?這就是這種情況下可能原型模式有比較好的表現。
3、當我們在處理一些對象比較簡單,并且對象之間的區別很小,可能只是很固定的幾個屬性不同的時候,可能我們使用原型模式更合適,例如我們生活中的彩虹的七彩的顏色,等等,我們只需要根據現有的一個顏色對象,克隆一個新的顏色對象,然后修改具體的顏色的值就可以滿足要求,然后如果通過我們之前講述的創建型工廠,抽象工廠模式等相對來說就引入新的依賴,并且復雜度也有所提高。例如我們的生活中的顏色的克隆:
[](http://images.cnblogs.com/cnblogs_com/hegezhou_hot/WindowsLiveWriter/3711c6dafbd3_12FBE/image_6.png)?我們都可以通過紅色來克隆其他的所有顏色,只是修改相應的個別屬性即可,遠比創建一個新的對象,然后給對象的各個屬性賦值來的簡單和方便,當然有的時候,如果我們并不需要基于現有的對象復制新的對象,或者我們需要的就是一個干凈的空對象,那么我的首先還是工廠模式或者抽象工廠模式啦。
?
### 五、深復制和淺復制
既然我們本篇講述了原型模式的具體應用,那么我們就必須先搞清楚深復制和淺復制,否則也沒有辦清楚原型模式中的具體的克隆過程和克隆出來的對象的詳細情況。
~~~
???? .NET Freamwork 內置的每個繼承自System.Object都有保護成員方法:
??????? //?
??????? // 摘要:?
??????? //???? 創建當前 System.Object 的淺表副本。?
??????? //?
??????? // 返回結果:?
??????? //???? 當前 System.Object 的淺表副本。?
??????? [SecuritySafeCritical]?
??????? protected object MemberwiseClone();
~~~
系統為我們內置提供了復制對象本身的方法,不過這個方法返回的是一個淺復制的對象副本,而且.NET給我提供了一個System.ICloneable的接口,
我們通過實現這個接口,可以為對象提供自定義的克隆方法。
為了搞明白淺復制和深復制,那么我先要搞懂這2者的區別,.NET本身提供了淺復制的方法,而深復制的方法需要自己實現接口來完成。
我們先來看看淺復制后的對象和對象副本的情況:
[](http://images.cnblogs.com/cnblogs_com/hegezhou_hot/WindowsLiveWriter/3711c6dafbd3_12FBE/image_8.png)
我們再看看看深復制的對象和對象副本的情況:
[](http://images.cnblogs.com/cnblogs_com/hegezhou_hot/WindowsLiveWriter/3711c6dafbd3_12FBE/image_12.png)?通過上面的描述,大家應該針對淺復制和深復制的區別有了大概的了解,那么我們再結合程序代碼來分析吧,可能大家會更熟悉具體的應用。我們先來看看最簡單的淺復制和深復制情況,我們這里舉例來說吧:
我們定義一個杯子類,并且簡單定義杯子的幾項簡單的屬性,具體代碼如下:
~~~
??? /// <summary>?
??? /// 杯子類?
??? /// </summary>?
??? public class Cup : ICloneable?
??? {?
??????? private double _rl;?
??????? private int _height;?
??????? private Factory _factory;?
??????? /// <summary>?
??????? /// 高度?
??????? /// </summary>?
??????? public int Height?
??????? {?
??????????? get?
??????????? {?
??????????????? return _height;?
??????????? }?
??????????? set?
??????????? {?
??????????????? _height = value;?
??????????? }?
??????? }
??????? /// <summary>?
??????? /// 容量?
??????? /// </summary>?
??????? public double RL?
??????? {?
??????????? get?
??????????? {?
??????????????? return _rl;?
??????????? }?
??????????? set?
??????????? {?
??????????????? _rl = value;?
??????????? }?
??????? }
??????? /// <summary>?
??????? /// 生產廠家?
??????? /// </summary>?
??????? public Factory Factory?
??????? {?
??????????? get?
??????????? {?
??????????????? return _factory;?
??????????? }?
??????????? set?
??????????? {?
??????????????? _factory = value;?
??????????? }?
??????? }
??????? #region ICloneable 成員
??????? public object Clone()?
??????? {?
??????????? return this.MemberwiseClone();?
??????? }
??????? #endregion?
??? }
~~~
具體的測試代碼:
~~~
class Program?
??? {?
??????? static void Main(string[] args)?
??????? {?
??????????? Cup cup = new Cup();?
??????????? cup.Height = 2;?
??????????? Cup cup1 = (Cup)cup.Clone();
??????????? cup1.Height = 1;?
??????????? Console.WriteLine(cup.Height == cup1.Height);?
??????????? System.Threading.Thread.Sleep(10000);?
??????? }?
??? }
~~~
運行結果如下:
[](http://images.cnblogs.com/cnblogs_com/hegezhou_hot/WindowsLiveWriter/3711c6dafbd3_12FBE/image_14.png)
綜上所述,我們知道,對于值類型的成員,淺復制也是在副本中重新創建的成員,對應到內存的棧上,分配新的內存空間。那么對于引用類型則因為淺復制的時候,對象和對象副本共用同一個引用對象,那么不管是在對象還是對象副本中修改了相應的引用成員了之后,那么這個引用類型的成員就會發生變化。
因為2個對象指向同一個內存地址,那么任何一個修改操作都會產生改變。
那么對于上面的這個類如何修改這個類的實現才能實現深復制呢?
將上面的Clone方法如下實現:
~~~
????? public object Clone()?
??????? {?
??????????? Cup cup = (Cup)this.MemberwiseClone();?
??????????? Factory factory1 = new Factory();?
??????????? factory1.FactoryName = this.Factory.FactoryName;?
??????????? cup.Factory = factory1;
??????????? return cup;?
??????? }
這樣就完成了對象的深復制,不管是值類型的成員還是引用類型的成員,這樣的對象和對象副本,對任何一個成員屬性的修改,都不會影響到改變對象的值。
### 六、原型模式的實現方案
#### 6.1 原型模式的經典實現
我們先來看看原型模式的經典實現,我們這里已顏色為例來說名下經典實現吧
定義一個接口, 用來表述所有的顏色對象接口:
~~~
??? public interface IColorDemo?
??? {?
??????? IColorDemo Clone();
??????? int Red?
??????? {?
??????????? get;?
??????????? set;?
??????? }?
??????? int Green?
??????? {?
??????????? get;?
??????????? set;?
??????? }?
??????? int Blue?
??????? {?
??????????? get;?
??????????? set;?
??????? }?
??? }
~~~
我們這里給出紅色的具體實現代碼:
~~~
??? public class RedColor : IColorDemo?
??? {?
??????? private int red;?
??????? private int green;?
??????? private int blue;?
??????? public int Red?
??????? {?
??????????? get?
??????????? {?
??????????????? return this.red;?
??????????? }?
??????????? set?
??????????? {?
??????????????? this.red = value;?
??????????? }?
??????? }?
??????? public int Green?
??????? {?
??????????? get?
??????????? {?
??????????????? return this.green;?
??????????? }?
??????????? set?
??????????? {?
??????????????? this.green = value;?
??????????? }?
??????? }?
??????? public int Blue?
??????? {?
??????????? get?
??????????? {?
??????????????? return this.blue;?
??????????? }?
??????????? set?
??????????? {?
??????????????? this.blue = value;?
??????????? }?
??????? }
??????? #region IColorDemo 成員
??????? public IColorDemo Clone()?
??????? {?
??????????? return (IColorDemo)this.MemberwiseClone();?
??????? }
??????? #endregion?
??? }
~~~
因為上面的對于顏色,都是通過RGB不同的比例配置出來的,所以我就定義了3個整形的變量,所以我這里只是演示說明。那么具體的測試代碼如下:
~~~
????? static void Main(string[] args)?
?????? {?
?????????? IColorDemo color = new RedColor();?
?????????? color.Red = 255;
?????????? IColorDemo color1 = color.Clone();?
?????????? color1.Blue = 255;
?????????? Console.WriteLine(color.Blue == color1.Blue);?
?????????? System.Threading.Thread.Sleep(10000);?
?????? }
~~~
返回的結果為false。代表對象副本的修改不會影響對象本身的狀態。
#### 6.2、原型模式的其他情況
上面講述了簡單的淺復制的情況,那么我們來分析下深復制原型的實現吧,深復制可能考慮的情況相對來說就會比較復雜,因為有可能對象是之間
有繼承關系或者引用關系的時候,可能我們深復制的時候就需要注意,當然這對我們也是個考驗。一般來說深復制一方面可以采用上面我給出的那種簡單的深復制對象的時候的方案,還可以通過序列化的形式來進行對象的復制。下面我們來通過序列化的形式來實現原型模式吧:
我們先給出序列化和反序列化的幫助類:
例如我們通過二進制的形式來進行序列化,我們都知道可以序列化的類必須打上標記,標識是否可以序列化,也可以在成員屬性上定義。
~~~
??? /// <summary>?
??? /// 序列化和反序列化輔助類?
??? /// </summary>?
???? public class SerializableHelper?
??? {?
???????? public string Serializable(object target)?
???????? {?
???????????? using (MemoryStream stream = new MemoryStream())?
???????????? {?
???????????????? new BinaryFormatter().Serialize(stream, target);
???????????????? return Convert.ToBase64String(stream.ToArray());?
???????????? }?
???????? }
???????? public object Derializable(string target)?
???????? {?
???????????? byte[] targetArray = Convert.FromBase64String(target);
???????????? using (MemoryStream stream = new MemoryStream(targetArray))?
???????????? {?
???????????????? return new BinaryFormatter().Deserialize(stream);?
???????????? }?
???????? }
???????? public T Derializable<T>(string target)?
???????? {?
???????????? return (T)Derializable(target);?
???????? }?
??? }
~~~
下面給出簡單的示例代碼,還是使用上面的顏色對象為例。我們修改顏色類中的Clone方法
~~~
??????? #region IColorDemo 成員
??????? public IColorDemo Clone()?
??????? {?
??????????? string target= SerializableHelper.Serializable(this);?
??????????? return SerializableHelper.Derializable<IColorDemo>(target);?
??????? }
??????? #endregion
?????? 程序的測試代碼如下:
?????? static void Main(string[] args)?
??????? {?
??????????? IColorDemo color = new RedColor();?
??????????? color.Red = 255;
??????????? IColorDemo color1 = color.Clone();?
??????????? color1.Red = 234;
??????????? Console.WriteLine(color.Blue == color1.Blue);?
??????????? System.Threading.Thread.Sleep(10000);?
??????? }
~~~
程序的運行結果為false,肯定二個對象是不同的,通過序列化和反序列化形成新的對象。其實只要是項目中要使用原型模式進行對象復制的情況下,都可以通過序列化的形式來進行深復制。
### 七、原型模式使用總結
原型模式作為創建型模式中的最特殊的一個模式,具體的創建過程,是由對象本身提供,這樣我們在很多的場景下,我們可以很方便的快速的構建新的對象,就像前面分析講解的幾類場景中,可能我們通過使用對象的克隆,比通過其他幾類的創建型模式,效果要好的多,而且代價也小很多。打個比方,原型模式對于系統的擴展,可以做到無縫的擴展,為什么這么說呢?比如其他的創建型工廠,如果新增一個對象類型,那么我們不管是修改配置文件的方式,還是修改代碼的形式,無疑我們都是需要進行修改的,對于我們大家通用的公共應用來說這無疑是危險的,那么通過原型模式,則可以解決這樣的問題,因為類型本身實現這樣的方法即可,但是也有一定的缺點,每個對象都實現這樣的方法,無疑是很大的工作量,但是在某些特殊的環境下,或者實際的項目中,可能原型模式是好的選擇。
### 八、系列進度
創建型
1、[系統架構技能之設計模式-單件模式](http://www.cnblogs.com/hegezhou_hot/archive/2010/10/02/1841390.html)
2、[系統架構技能之設計模式-工廠模式](http://www.cnblogs.com/hegezhou_hot/archive/2010/11/30/1892227.html)
3、[系統架構技能之設計模式-抽象工廠模式](http://www.cnblogs.com/hegezhou_hot/archive/2010/12/01/1893388.html)
4、[系統架構技能之設計模式-創建者模式](http://www.cnblogs.com/hegezhou_hot/archive/2010/12/02/1894771.html)
5、[系統架構技能之設計模式-原型模式](http://www.cnblogs.com/hegezhou_hot/archive/2010/12/04/1896471.html)
結構型
1、系統架構技能之設計模式-組合模式
2、系統架構技能之設計模式-外觀模式
3、系統架構技能之設計模式-適配器模式
4、系統架構技能之設計模式-橋模式
5、系統架構技能之設計模式-裝飾模式
6、系統架構技能之設計模式-享元模式
7、系統架構技能之設計模式-代理模式
行為型
1、系統架構技能之設計模式-命令模式
2、系統架構技能之設計模式-觀察者模式
3、系統架構技能之設計模式-策略模式
4、系統架構技能之設計模式-職責模式
5、系統架構技能之設計模式-模板模式
6、系統架構技能之設計模式-中介者模式
7、系統架構技能之設計模式-解釋器模式
### 九、下篇預告
下篇將會針對外觀模式進行講述,該模式也是結構型模式中很有特點設計模式之一,該 模式是將現有系統中的一些細粒度的東西通過外觀對象包裝起來,在應用程序中訪問這些方法的時候,通過外觀類的形式,提供統一的訪問入口,并且具體的細節,應用程序并不需要知道,這樣就會降低程序調用的復雜性,由于本人水平有限,不足或者有錯誤的地方,請大家批評指正,請大家繼續支持我,謝謝。
### 十、Demo下載
[下載本文Demo](http://files.cnblogs.com/hegezhou_hot/ProtoTypical.rar)