上篇建立者模式中,我們主要講述了建立者的幾類實作方案,和建立者模式的應用的場景和特點,建立者模式适合建立複雜的對象,并且這些對象的每 個組成部分的詳細建立步驟可以是動态的變化的,但是每個對象的組裝的過程來說可能是相對固定的或者說是對象的建立的過程是固定的,那麼通過建立者 模式可以很好的解決這類複雜對象的建立,而在我們的生活中或者是項目中可能會有這個方面的需求,那麼使用建立者模式無疑是好的選擇。 建立者模式中的每個對象組成部分的建構都是對象本身提供的内部方法,具體的建立者隻是調用要建立的對象的内部的相應組成部分的建構方法,組 織這些對象内部建構方法的執行順序,完成對象的完整建構。當我們的客戶應用程式需要調用這個建立者時,我們隻需要通過指導者調用的形式,提供統一 的建立者通路入口,通過構造函數注入或者配置檔案的形式來完成建立者的注入。
本文主要是講述建立型模式中一個比較特殊的模式-原型模式,這個模式呢,有個最大的特點是克隆一個現有的對象,這個克隆的結果有2種,一種是 是淺複制,另一種是深複制,這裡我們也會探讨下深複制和淺複制的原理,這樣可能更友善大家了解這個原型模式的使用。我們都知道,建立型模式一般是 用來建立一個新的對象,然後我們使用這個對象完成一些對象的操作,我們通過原型模式可以快速的建立一個對象而不需要提供專門的new()操作就可以快 速完成對象的建立,這無疑是一種非常有效的方式,快速的建立一個新的對象。本文将會從以下幾個方面進行講述: 1、原型模式的使用場景和特點 2、淺複制和深複制的原理。 3、舉例說明淺複制和深複制。 4、原型模式的實作方案。 5、總結原型模式。 我們這裡先給出一個原型模式的原理圖:
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語句,不包含資料列沒有發生變化的列,不要出現在更新語句中,這個時候,可能一個方案會是,編輯前克隆一個對象,然後等編輯後送出 的時候,生成相應的語句時進行對比之前克隆的對象,看看是否資料發生變化,如果說對象的部分資料列發生變化,那麼就隻是把變化的資料列進行更新。 當然上面我隻是給出了一種比較簡單的,但是效率不是很高的實作方案,還有很多好的方案我就不讨論了,這裡隻是為了說明原型模式的可用場景。 如果對上面的方式不是很了解或者看文字比較累的話,可以看下面的圖,應該就比較清晰了。 3、當我們在處理一些對象比較簡單,并且對象之間的差別很小,可能隻是很固定的幾個屬性不同的時候,可能我們使用原型模式更合适,例如我們生 活中的彩虹的七彩的顔色,等等,我們隻需要根據現有的一個顔色對象,克隆一個新的顔色對象,然後修改具體的顔色的值就可以滿足要求,然後如果通過 我們之前講述的建立型工廠,抽象工廠模式等相對來說就引入新的依賴,并且複雜度也有所提高。例如我們的生活中的顔色的克隆: 象,然後給對象的各個屬性指派來的簡單和友善,當然有的時候,如果我們并不需要基于現有的對象複制新的對象,或者我們需要的就是一個幹淨的空對 象,那麼我的首先還是工廠模式或者抽象工廠模式啦。
既然我們本篇講述了原型模式的具體應用,那麼我們就必須先搞清楚深複制和淺複制,否則也沒有辦清楚原型模式中的具體的克隆過程和克隆出來的 對象的詳細情況。 .NET Freamwork 内置的每個繼承自System.Object都有保護成員方法: // // 摘要: // 建立目前 System.Object 的淺表副本。 // 傳回結果: // 目前 System.Object 的淺表副本。 [SecuritySafeCritical] protected object MemberwiseClone(); 系統為我們内置提供了複制對象本身的方法,不過這個方法傳回的是一個淺複制的對象副本,而且.NET給我提供了一個System.ICloneable的接口, 我們通過實作這個接口,可以為對象提供自定義的克隆方法。 為了搞明白淺複制和深複制,那麼我先要搞懂這2者的差別,.NET本身提供了淺複制的方法,而深複制的方法需要自己實作接口來完成。 我們先來看看淺複制後的對象和對象副本的情況: 我們再看看看深複制的對象和對象副本的情況: 析吧,可能大家會更熟悉具體的應用。我們先來看看最簡單的淺複制和深複制情況,我們這裡舉例來說吧: 我們定義一個杯子類,并且簡單定義杯子的幾項簡單的屬性,具體代碼如下: /// <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; } /// 容量 public double RL return _rl; _rl = value; /// 生産廠家 public Factory Factory return _factory; _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); } 運作結果如下: <a href="http://images.cnblogs.com/cnblogs_com/hegezhou_hot/WindowsLiveWriter/3711c6dafbd3_12FBE/image_14.png"></a> 綜上所述,我們知道,對于值類型的成員,淺複制也是在副本中重新建立的成員,對應到記憶體的棧上,配置設定新的記憶體空間。那麼對于引用類型則因為淺複制 的時候,對象和對象副本共用同一個引用對象,那麼不管是在對象還是對象副本中修改了相應的引用成員了之後,那麼這個引用類型的成員就會發生變化。 因為2個對象指向同一個記憶體位址,那麼任何一個修改操作都會産生改變。 那麼對于上面的這個類如何修改這個類的實作才能實作深複制呢? 将上面的Clone方法如下實作: public object Clone() Cup cup = (Cup)this.MemberwiseClone(); Factory factory1 = new Factory(); factory1.FactoryName = this.Factory.FactoryName; cup.Factory = factory1; return cup; 這樣就完成了對象的深複制,不管是值類型的成員還是引用類型的成員,這樣的對象和對象副本,對任何一個成員屬性的修改,都不會影響到改變對象的 值。
我們先來看看原型模式的經典實作,我們這裡已顔色為例來說名下經典實作吧 定義一個接口, 用來表述所有的顔色對象接口:
public interface IColorDemo IColorDemo Clone(); int Red get; set; int Green int Blue 我們這裡給出紅色的具體實作代碼: public class RedColor : IColorDemo private int red; private int green; private int blue; public int Red return this.red; this.red = value; public int Green return this.green; this.green = value; public int Blue return this.blue; this.blue = value; #region IColorDemo 成員 public IColorDemo Clone() return (IColorDemo)this.MemberwiseClone(); 因為上面的對于顔色,都是通過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。代表對象副本的修改不會影響對象本身的狀态。
上面講述了簡單的淺複制的情況,那麼我們來分析下深複制原型的實作吧,深複制可能考慮的情況相對來說就會比較複雜,因為有可能對象是之間 有繼承關系或者引用關系的時候,可能我們深複制的時候就需要注意,當然這對我們也是個考驗。一般來說深複制一方面可以采用上面我給出的那種簡單的 深複制對象的時候的方案,還可以通過序列化的形式來進行對象的複制。下面我們來通過序列化的形式來實作原型模式吧:
我們先給出序列化和反序列化的幫助類:
例如我們通過二進制的形式來進行序列化,我們都知道可以序列化的類必須打上标記,辨別是否可以序列化,也可以在成員屬性上定義。
/// 序列化和反序列化輔助類 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方法 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); 程式的運作結果為false,肯定二個對象是不同的,通過序列化和反序列化形成新的對象。其實隻要是項目中要使用原型模式進行對象複制的情況 下,都可以通過序列化的形式來進行深複制。
原型模式作為建立型模式中的最特殊的一個模式,具體的建立過程,是由對象本身提供,這樣我們在很多的場景下,我們可以很友善的快速的建構新 的對象,就像前面分析講解的幾類場景中,可能我們通過使用對象的克隆,比通過其他幾類的建立型模式,效果要好的多,而且代價也小很多。打個比方, 原型模式對于系統的擴充,可以做到無縫的擴充,為什麼這麼說呢?比如其他的建立型工廠,如果新增一個對象類型,那麼我們不管是修改配置檔案的方 式,還是修改代碼的形式,無疑我們都是需要進行修改的,對于我們大家通用的公共應用來說這無疑是危險的,那麼通過原型模式,則可以解決這樣的問 題,因為類型本身實作這樣的方法即可,但是也有一定的缺點,每個對象都實作這樣的方法,無疑是很大的工作量,但是在某些特殊的環境下,或者實際的 項目中,可能原型模式是好的選擇。
下篇将會針對外觀模式進行講述,該模式也是結構型模式中很有特點設計模式之一,該 模式是将現有系統中的一些細粒度的東西通過外觀對象包裝起來, 在應用程式中通路這些方法的時候,通過外觀類的形式,提供統一的通路入口,并且具體的細節,應用程式并不需要知道,這樣就會降低程式調用的複雜 性,由于本人水準有限,不足或者有錯誤的地方,請大家批評指正,請大家繼續支援我,謝謝。
本文轉自 hot的fans 51CTO部落格,原文連結:http://blog.51cto.com/2435232/603398