前面講了建立一個對象實例的方法單例模式Singleton Pattern, 創造多個產品的工廠模式(簡單工廠模式 Simple Factory Pattern, 工廠方法模式 FactoryMothed Pattern,抽象工廠模式 Abstract Factory Method),以及建立複雜對象的建造者模式 Builder Pattern, 這几几乎包含了產品建立的各個方方面,可是還有一種,那就是有自我建立能力的模式,這種模式可以建立出和本身相同或者類似的對象。生活中常常也會見到這方面的例子,好比蠕蟲病毒的自我複製,細胞分裂以及自我繁殖,遊戲角色的自我複製和分身等。html
在軟件開發中也會常常遇到這樣的問題,最近在作項目的時候就碰到了這麼一個需求,問卷調查試卷複用的問題。咱們的系統用戶組成是這樣的,系統有一個超級管理員的角色,超級管理員能夠幹任何事情,系統中還接入了N多公司,每一個公司有公司的管理員,公司管理員能夠幹超級管理員分配給該公司的相應權限, 那麼這個問卷調查的需求是這樣的:數據庫
1. 超級管理員能夠建立問卷調查試卷,可是這個問卷調查的試卷不能直接使用,只能供各公司的管理員做爲模板樣例建立本身公司的問卷調查試卷。編程
2.各個公司的管理員能夠建立本身公司的問卷調查試卷,僅供本身公司員工使用。設計模式
3.各個公司的管理員能夠能夠基於超級管理員建立的問卷調查試卷建立本身的模板,建立出來的問卷調查試卷僅供本身公司的員工使用。網絡
4.公司管理員不能修改超級管理員建立的調查問卷試卷。app
5. 超級管理員能夠修改本身的問卷調查試卷, 而且不會影響各個公司以前根據問卷調查試卷建立出來的試卷。dom
需求用文字描述出來有點費勁,看下面這張圖:ide
該怎麼來實現呢? 這就是本文要討論的主角原型模式Pototype Pattern ,也是最後一個建立型模式,原型模式就是爲解決這類問題而生的:)。函數
原型模式(Prototype Pattern):使用原型實例指定建立對象的種類,而且經過拷貝這些原型建立新的對象。原型模式是一種對象建立型模式ui
它是聲明克隆方法的接口,是全部具體原型類的公共父類,能夠是抽象類也能夠是接口,甚至還能夠是具體實現類。
它實如今抽象原型類中聲明的克隆方法,在克隆方法中返回本身的一個克隆對象。
讓一個原型對象克隆自身從而建立一個新的對象,在客戶類中只須要直接實例化或經過工廠方法等方式建立一個原型對象,再經過調用該對象的克隆方法便可獲得多個相同的對象。因爲客戶類針對抽象原型類Prototype編程,所以用戶能夠根據須要選擇具體原型類,系統具備較好的可擴展性,增長或更換具體原型類都很方便。
原型模式的核心是克隆方法的實現:
通用的克隆實現方法是在具體原型類的克隆方法中實例化一個與自身類型相同的對象並將其返回,並將相關的參數傳入新建立的對象中,保證它們的成員屬性相同:
public abstract class Pototype { public string Some { get; set; } public abstract Pototype Clone(); } public class ConcretePototype : Pototype { public override Pototype Clone() { Pototype pototype = new ConcretePototype(); pototype.Some = this.Some; return pototype; } }
客戶端調用:
static void Main(string[] args) { Structure.Pototype pototype = new ConcretePototype(); pototype.Some = "pototype"; Structure.Pototype clone = pototype.Clone(); Console.WriteLine("I'm old "+pototype.Some); Console.WriteLine("I'm clone "+clone.Some); Console.ReadKey(); }
輸出結果:
C# 中有一個實現拷貝的方法MemberwiseClone,在克隆方法中咱們用MemberwiseClone來實現克隆一個對象:
public class CsharpPototype : Pototype { public override Pototype Clone() { return this.MemberwiseClone() as Pototype; } }
客戶端調用:
static void Main(string[] args) { Structure.Pototype pototype = new CsharpPototype(); pototype.Some = "pototype"; Structure.Pototype clone = pototype.Clone(); Console.WriteLine("I'm old "+pototype.Some); Console.WriteLine("I'm clone "+clone.Some); Console.ReadKey(); }
客戶端輸出和上面同樣, 這種方式實現的是淺拷貝。
深拷貝和淺拷貝, 淺拷貝若是原型對象的成員是值類型那麼就將原型對象複製一份給克隆對象,若是是引用類型,只將原型對象的地址複製一份給克隆對象,這時克隆對象和原型對象在內存中指向同一個對象,修改其中的一個會影響另外一個。深拷貝,則是將原型對象拷貝一份給克隆對象,克隆對象和原型對象在內存中有獨立的存儲空間,一個改動了不會影響另外一個。
如今咱們弄明白了原型模式,如今就來實現開頭提出的問卷調查試卷建立的需求,由於試卷的內容比較複雜這裏咱們是找出一些核心的可以說明問題的模型來演示這個例子。咱們先將試卷的內容假定爲字符串類型。
這裏咱們將超級管理員建立的試卷命名爲SuperSurveyPaper。
這裏SuperSurveyPaper 充當抽象原型類,CompanyASurveyPaper 和CompanyBSurveyPaper 充當具體原型類。
public abstract class SuperSurveyPaper { public string Name { get; set; } public string Content { get; set; } public abstract SuperSurveyPaper Clone(); } public class CompanyASurveyPaper : SuperSurveyPaper { public override SuperSurveyPaper Clone() { return this.MemberwiseClone() as SuperSurveyPaper; } } public class CompanyBSurveyPaper : SuperSurveyPaper { public override SuperSurveyPaper Clone() { return this.MemberwiseClone() as SuperSurveyPaper; } }
客戶端調用代碼:
static void Main(string[] args) { PototypeInstance.SuperSurveyPaper pototype = new CompanyASurveyPaper(); pototype.Name = "SuperSurveyPaper ->Name"; pototype.Content = "SuperSurveyPaper -> Content"; PototypeInstance.SuperSurveyPaper clone = pototype.Clone(); Console.WriteLine("I'm old Name: " + pototype.Name); Console.WriteLine("I'm old Content: " + pototype.Content); Console.WriteLine("======================== ==========="); Console.WriteLine("I'm Clone Name: " + clone.Name); Console.WriteLine("I'm Clone Content: " + clone.Content); Console.ReadKey(); }
輸出結果:
這裏也能夠加入配置經過反射來建立原型對象
在app.config 中加入配置:
<appSettings> <add key="Pototype" value="DesignPattern.Pototype.PototypeInstance.CompanyBSurveyPaper"/> </appSettings>
調用段代碼改爲:
static void Main(string[] args) { PototypeInstance.SuperSurveyPaper pototype; var setting = ConfigurationSettings.AppSettings["Pototype"]; var obj = Type.GetType(setting); if (obj == null) return; pototype = Activator.CreateInstance(obj) as PototypeInstance.SuperSurveyPaper; if (pototype == null) return; pototype.Name = "SuperSurveyPaper ->Name"; pototype.Content = "SuperSurveyPaper -> Content"; PototypeInstance.SuperSurveyPaper clone = pototype.Clone(); Console.WriteLine("I'm old Name: " + pototype.Name); Console.WriteLine("I'm old Content: " + pototype.Content); Console.WriteLine("======================== ==========="); Console.WriteLine("I'm Clone Name: " + clone.Name); Console.WriteLine("I'm Clone Content: " + clone.Content); Console.ReadKey(); }
輸出結果和上面同樣
若是如今的試卷內容不是一個簡單的字符串了而是一個對象:
[Serializable] public class SurveyPaperModel { public string FirstName { get; set; } public string LastName { get; set; } } [Serializable] public abstract class SuperSurveyPaper { public string Name { get; set; } public SurveyPaperModel Content { get; set; } public abstract SuperSurveyPaper Clone(); } [Serializable] public class CompanyASurveyPaper : SuperSurveyPaper { public override SuperSurveyPaper Clone() { MemoryStream memoryStream = new MemoryStream(); BinaryFormatter formatter = new BinaryFormatter(); formatter.Serialize(memoryStream, this); memoryStream.Position = 0; return formatter.Deserialize(memoryStream) as SuperSurveyPaper; } } [Serializable] public class CompanyBSurveyPaper : SuperSurveyPaper { public override SuperSurveyPaper Clone() { MemoryStream memoryStream = new MemoryStream(); BinaryFormatter formatter = new BinaryFormatter(); formatter.Serialize(memoryStream, this); memoryStream.Position = 0; return formatter.Deserialize(memoryStream) as SuperSurveyPaper; } }
客戶端調用:
static void Main(string[] args) { PototypeInstance.SuperSurveyPaper pototype; var setting = ConfigurationSettings.AppSettings["Pototype"]; var obj = Type.GetType(setting); if (obj == null) return; pototype = Activator.CreateInstance(obj) as PototypeInstance.SuperSurveyPaper; if (pototype == null) return; pototype.Name = "SuperSurveyPaper ->Name"; pototype.Content = new SurveyPaperModel { FirstName = "Design", LastName = "Pattern" }; PototypeInstance.SuperSurveyPaper clone = pototype.Clone(); Console.WriteLine("I'm old Name: " + pototype.Name); Console.WriteLine("I'm old Content: " + pototype.Content.FirstName); Console.WriteLine("I'm old Content: " + pototype.Content.LastName); Console.WriteLine("======================== ==========="); Console.WriteLine("I'm Clone Name: " + clone.Name); Console.WriteLine("I'm Clone Content: " + clone.Content.FirstName); Console.WriteLine("I'm Clone Content: " + clone.Content.LastName); Console.WriteLine("pototype==clone:" + clone.Equals(pototype)); Console.ReadKey(); }
輸出結果:
原型管理器(Prototype Manager)是將多個原型對象存儲在一個集合中供客戶端使用,它是一個專門負責管理克隆對象的工廠,其中定義了一個集合用於存儲原型對象,若是須要某個原型對象的一個克隆,能夠在集合中找到該原型對象並克隆一個新對象。在原型管理器中針對抽象原型類進行編程,便於擴展。
仍是應用上面的例子,如今加入B公司:
[Serializable] public class SurveyPaperModel { public string FirstName { get; set; } public string LastName { get; set; } } [Serializable] public abstract class SuperSurveyPaper { public string Name { get; set; } public SurveyPaperModel Content { get; set; } public abstract SuperSurveyPaper Clone(); } [Serializable] public class CompanyASurveyPaper : SuperSurveyPaper { public override SuperSurveyPaper Clone() { MemoryStream memoryStream = new MemoryStream(); BinaryFormatter formatter = new BinaryFormatter(); formatter.Serialize(memoryStream, this); memoryStream.Position = 0; return formatter.Deserialize(memoryStream) as SuperSurveyPaper; } } [Serializable] public class CompanyBSurveyPaper : SuperSurveyPaper { public override SuperSurveyPaper Clone() { MemoryStream memoryStream = new MemoryStream(); BinaryFormatter formatter = new BinaryFormatter(); formatter.Serialize(memoryStream, this); memoryStream.Position = 0; return formatter.Deserialize(memoryStream) as SuperSurveyPaper; } } public class PototypeManager { static IDictionary<string, SuperSurveyPaper> superSurveyPapers = new Dictionary<string, SuperSurveyPaper>(); static PototypeManager() { SuperSurveyPaper companyASurveyPaper = new CompanyASurveyPaper(); companyASurveyPaper.Name = "Company A"; companyASurveyPaper.Content= new SurveyPaperModel {FirstName="Michael",LastName="Du"}; SuperSurveyPaper companyBSurveyPaper = new CompanyBSurveyPaper(); companyBSurveyPaper.Name = "Company B"; companyBSurveyPaper.Content = new SurveyPaperModel { FirstName = "Kevin", LastName = "Durant" }; superSurveyPapers.Add("CompanyA", companyASurveyPaper); superSurveyPapers.Add("CompanyB", companyBSurveyPaper); } private PototypeManager (){} public SuperSurveyPaper GetSuperPaper(string key){ return superSurveyPapers[key].Clone(); } public void RegisterSurveyPaper(string key, SuperSurveyPaper ssp){ superSurveyPapers.Add(key, ssp); } public static PototypeManager Instance { get{ return PototypeManagerInitializer.instance;} } private static class PototypeManagerInitializer { public static readonly PototypeManager instance=new PototypeManager(); } }
這裏的PototypeManager類使用了一個Singleton模式建立出來,在靜態構造裏初始化了原型對象,並將其註冊在一個字典中,這個在項目中數據是從數據庫中直接讀取的。這個管理類還暴露了一個註冊原型實例的方法,便於擴展和動態給管理器增長原型對象。在獲取Clone對象的方法中直接將原型對象的一個Copy返回給客戶程序。確保客戶端獲得的對象是一個全新的對象。
客戶端調用代碼:
static void Main(string[] args) { PototypeInstance.SuperSurveyPaper pototype1, pototype2, pototype3, pototype4; pototype1 = PototypeManager.Instance.GetSuperPaper("CompanyA"); pototype2 = PototypeManager.Instance.GetSuperPaper("CompanyA"); Console.WriteLine("I'm old Name: " + pototype1.Name); Console.WriteLine("I'm old Content: " + pototype1.Content.FirstName); Console.WriteLine("I'm old Content: " + pototype1.Content.LastName); Console.WriteLine("I'm Clone Name: " + pototype2.Name); Console.WriteLine("I'm Clone Content: " + pototype2.Content.FirstName); Console.WriteLine("I'm Clone Content: " + pototype2.Content.LastName); Console.WriteLine("pototype1==pototype2:" + pototype2.Equals(pototype1)); Console.WriteLine("======================== ==========="); pototype3 = PototypeManager.Instance.GetSuperPaper("CompanyB"); pototype4 = PototypeManager.Instance.GetSuperPaper("CompanyB"); Console.WriteLine("I'm old Name: " + pototype3.Name); Console.WriteLine("I'm old Content: " + pototype3.Content.FirstName); Console.WriteLine("I'm old Content: " + pototype3.Content.LastName); Console.WriteLine("I'm Clone Name: " + pototype4.Name); Console.WriteLine("I'm Clone Content: " + pototype4.Content.FirstName); Console.WriteLine("I'm Clone Content: " + pototype4.Content.LastName); Console.WriteLine("pototype3==pototype4:" + pototype4.Equals(pototype3)); Console.ReadKey(); }
輸出結果:
使用原型模式的「自我」複製能力,咱們能夠很容易的實現,建立副本和撤銷副本的功能, 在控制檯中咱們輸入c 替代ctrl+c,輸入:z 替代ctrl+z 來模擬這個拷貝和撤銷的過程,首先咱們建立一個原型對象,每次按C的時候使用最後clone出來的對象再克隆新的對象,並把這些對象依次保存在一個list中,當按Z的時候咱們依次在list中移除最後加入的對象直到起初建立的原型對象爲止, 簡單的客戶端代碼實現以下:
static List<PototypeInstance.SuperSurveyPaper> _list = new List<PototypeInstance.SuperSurveyPaper>(); static List<SurveyPaperModel> _listModel = new List<SurveyPaperModel> { new SurveyPaperModel{FirstName="Terry",LastName="Go"}, new SurveyPaperModel{FirstName="Ke",LastName="Be"}, new SurveyPaperModel{FirstName="Lebron",LastName="Jimes"}, new SurveyPaperModel{FirstName="Steve",LastName="Jo"}, new SurveyPaperModel{FirstName="Stive",LastName="Kurry"}, new SurveyPaperModel{FirstName="Henry",LastName="He"}, new SurveyPaperModel{FirstName="Kevin",LastName="Druant"}, new SurveyPaperModel{FirstName="Blue",LastName="Jhon"}, new SurveyPaperModel{FirstName="Jerry",LastName="Ma"}, new SurveyPaperModel{FirstName="Fred",LastName="Gao"}, }; static void Main(string[] args) { PototypeInstance.SuperSurveyPaper pototype1; pototype1 = PototypeManager.Instance.GetSuperPaper("CompanyB"); Console.WriteLine("I'm old Name: " + pototype1.Name); Console.WriteLine("I'm old Content: " + pototype1.Content.FirstName); Console.WriteLine("I'm old Content: " + pototype1.Content.LastName); _list.Add(pototype1); while (true) { var key = Console.ReadKey(); switch (key.Key) { case ConsoleKey.C: var pototypeLastInstance = _list.Last<PototypeInstance.SuperSurveyPaper>(); var cloneFromPototypeLastInstance = pototypeLastInstance.Clone(); cloneFromPototypeLastInstance.Name = "Version " + _list.Count; Random rd = new Random(); cloneFromPototypeLastInstance.Content = _listModel[rd.Next(0, _listModel.Count)]; _list.Add(cloneFromPototypeLastInstance); PrintList(_list); break; case ConsoleKey.Z: if (_list.Count > 1) _list.RemoveAt(_list.Count - 1); PrintList(_list); break; case ConsoleKey.Q: return; } } Console.ReadKey(); } static void PrintList(List<PototypeInstance.SuperSurveyPaper> list) { Console.WriteLine("========="); var pototpe = list.Last(); Console.WriteLine("History:" + pototpe.Name); Console.WriteLine("I'm Firstname: " + pototpe.Content.FirstName); Console.WriteLine("I'm LastName: " + pototpe.Content.LastName); }
客戶端輸出:
好了,到這裏設計模式的建立型模式就所有討論完了。下面接着討論結構型模式。