建立型模式就是用來建立對象的模式,抽象了實例化的過程。全部的建立型模式都有兩個共同點。第一,它們都將系統使用哪些具體類的信息封裝起來;第二,它們隱藏了這些類的實例是如何被建立和組織的。建立型模式包括單例模式、工廠方法模式、抽象工廠模式、建造者模式和原型模式。編程
單例模式指的是確保某一個類只有一個實例,並提供一個全局訪問點。解決的是實體對象個數的問題,而其餘的建造者模式都是解決new所帶來的耦合關係問題。其實現要點有:設計模式
類只有一個實例。問:如何保證呢?答:經過私有構造函數來保證類外部不能對類進行實例化ide
提供一個全局的訪問點。問:如何實現呢?答:建立一個返回該類對象的靜態方法函數
具體的代碼就很少說了,這個比較經常使用。工具
簡單工廠類示例代碼 /// <summary> /// 簡單工廠類, 負責 炒菜 /// </summary> public class FoodSimpleFactory { public static Food CreateFood(string type) { Food food = null; if (type.Equals("土豆肉絲")) { food= new ShreddedPorkWithPotatoes(); } else if (type.Equals("西紅柿炒蛋")) { food= new TomatoScrambledEggs(); } return food; } } //調用 Food food1 = FoodSimpleFactory.CreateFood("西紅柿炒蛋"); food1.Print();
簡單工廠模式解決了客戶端直接依賴於具體對象的問題,客戶端能夠消除直接建立對象的責任,而僅僅是消費產品。簡單工廠模式實現了對責任的分割。簡單工廠模式又叫靜態方法模式(由於工廠類都定義了一個靜態方法),由一個工廠類根據傳入的參數決定建立出哪種產品類的實例。ui
系統擴展困難,一旦添加新產品就不得不修改工廠邏輯,這樣就會形成工廠邏輯過於複雜。this
當工廠類負責建立的對象比較少時能夠考慮使用簡單工廠模式(),客戶若是隻知道傳入工廠類的參數,對於如何建立對象的邏輯不關心時能夠考慮使用簡單工廠模式spa
.NET中System.Text.Encoding類就實現了簡單工廠模式,該類中的GetEncoding(int codepage)就是工廠方法.net
工廠方法模式指的是定義一個建立對象的工廠接口,由其子類決定要實例化的類,將實際建立工做推遲到子類中。它強調的是」單個對象「的變化。其實現要點有:prototype
建立派生於工廠抽象類,即由具體工廠去建立具體產品,既然要建立產品,天然須要產品抽象類和具體產品類了。
其具體的UML結構圖以下所示:
在工廠方法模式中,工廠類與具體產品類具備平行的等級結構,它們之間是一一對應關係。
示例代碼 /// <summary> /// 抽象工廠類 /// </summary> public abstract class Creator { // 工廠方法 public abstract Food CreateFoddFactory(); } /// <summary> /// 西紅柿炒蛋工廠類 /// </summary> public class TomatoScrambledEggsFactory:Creator { /// <summary> /// 負責建立西紅柿炒蛋這道菜 /// </summary> /// <returns></returns> public override Food CreateFoddFactory() { return new TomatoScrambledEggs(); } } /// <summary> /// 土豆肉絲工廠類 /// </summary> public class ShreddedPorkWithPotatoesFactory:Creator { /// <summary> /// 負責建立土豆肉絲這道菜 /// </summary> /// <returns></returns> public override Food CreateFoddFactory() { return new ShreddedPorkWithPotatoes(); } } //調用代碼 // 初始化作菜的兩個工廠() Creator shreddedPorkWithPotatoesFactory = new ShreddedPorkWithPotatoesFactory(); Creator tomatoScrambledEggsFactory = new TomatoScrambledEggsFactory(); // 開始作西紅柿炒蛋 Food tomatoScrambleEggs = tomatoScrambledEggsFactory.CreateFoddFactory(); tomatoScrambleEggs.Print(); //開始作土豆肉絲 Food shreddedPorkWithPotatoes = shreddedPorkWithPotatoesFactory.CreateFoddFactory(); shreddedPorkWithPotatoes.Print();
.NET 類庫中也有不少實現了工廠方法的類,例如Asp.net中,處理程序對象是具體用來處理請求,當咱們請求一個*.aspx的文件時,此時會映射到System.Web.UI.PageHandlerFactory類上進行處理,而對*.ashx的請求將映射到System.Web.UI.SimpleHandlerFactory類中(這兩個類都是繼承於IHttpHandlerFactory接口的)。
工廠方法模式經過面向對象編程中的多態性來將對象的建立延遲到具體工廠中,從而解決了簡單工廠模式中存在的問題,也很好地符合了開放封閉原則(即對擴展開發,對修改封閉)。
上面的工廠方法模式是爲了克服簡單工廠模式的缺點而設計出來的,簡單工廠模式的工廠類隨着產品類的增長鬚要增長額外的代碼),而工廠方法模式每一個具體工廠類只完成單個實例的建立,因此它具備很好的可擴展性。可是在現實生活中,一個工廠只建立單個產品這樣的例子不多,由於如今的工廠都多元化了,一個工廠建立一系列的產品,若是咱們要設計這樣的系統時,工廠方法模式顯然在這裏不適用,而後抽象工廠模式卻能夠很好地解決一系列產品建立的問題,
抽象工廠模式指的是提供一個建立一系列相關或相互依賴對象的接口,使得客戶端能夠在沒必要指定產品的具體類型的狀況下,建立多個產品族中的產品對象,強調的是」系列對象「的變化。其實現要點有:提供一系列對象的接口。問:如何去實現呢?答:提供多個產品的抽象接口,建立多個產品族中的多個產品對象。問:如何作到呢?答:每一個具體工廠建立一個產品族中的多個產品對象,多個具體工廠就能夠建立多個產品族中的多個對象了。
具體的UML結構圖以下所示:
下面就以生活中 「絕味」 連鎖店的例子來實現一個抽象工廠模式。例如,絕味鴨脖想在江西南昌和上海開分店,可是因爲當地人的口味不同,在南昌的全部絕味的東西會作的辣一點,而上海不喜歡吃辣的,因此上海的全部絕味的東西都不會作的像南昌的那樣辣,然而這點不一樣致使南昌絕味工廠和上海的絕味工廠生成全部絕味的產品都不一樣
/// <summary> /// 抽象工廠類,提供建立兩個不一樣地方的鴨架和鴨脖的接口 /// </summary> public abstract class AbstractFactory { // 抽象工廠提供建立一系列產品的接口,這裏做爲例子,只給出了絕味中鴨脖和鴨架的建立接口 public abstract YaBo CreateYaBo(); public abstract YaJia CreateYaJia(); } /// <summary> /// 南昌絕味工廠負責製做南昌的鴨脖和鴨架 /// </summary> public class NanChangFactory : AbstractFactory { // 製做南昌鴨脖 public override YaBo CreateYaBo() { return new NanChangYaBo(); } // 製做南昌鴨架 public override YaJia CreateYaJia() { return new NanChangYaJia(); } } /// <summary> /// 上海絕味工廠負責製做上海的鴨脖和鴨架 /// </summary> public class ShangHaiFactory : AbstractFactory { // 製做上海鴨脖 public override YaBo CreateYaBo() { return new ShangHaiYaBo(); } // 製做上海鴨架 public override YaJia CreateYaJia() { return new ShangHaiYaJia(); } } /// <summary> /// 鴨脖抽象類,供每一個地方的鴨脖類繼承 /// </summary> public abstract class YaBo { /// <summary> /// 打印方法,用於輸出信息 /// </summary> public abstract void Print(); } /// <summary> /// 鴨架抽象類,供每一個地方的鴨架類繼承 /// </summary> public abstract class YaJia { /// <summary> /// 打印方法,用於輸出信息 /// </summary> public abstract void Print(); } //調用代碼 AbstractFactory nanChangFactory = new NanChangFactory(); YaBo nanChangYabo = nanChangFactory.CreateYaBo(); nanChangYabo.Print(); YaJia nanChangYajia= nanChangFactory.CreateYaJia(); nanChangYajia.Print(); // 上海工廠製做上海的鴨脖和鴨架 AbstractFactory shangHaiFactory = new ShangHaiFactory(); shangHaiFactory.CreateYaBo().Print(); shangHaiFactory.CreateYaJia().Print();
抽象工廠模式將具體產品的建立延遲到具體工廠的子類中,這樣將對象的建立封裝起來,能夠減小客戶端與具體產品類之間的依賴,從而使系統耦合度低,這樣更有利於後期的維護和擴展,這是抽象工廠模式的優勢所在,而後抽象模式同時也存在不足的地方。下面就具體看下抽象工廠的缺點(缺點其實在前面的介紹中以已經涉及了):
抽象工廠模式很難支持新種類產品的變化。這是由於抽象工廠接口中已經肯定了能夠被建立的產品集合,若是須要添加新產品,此時就必須去修改抽象工廠的接口,這樣就涉及到抽象工廠類的以及全部子類的改變,這樣也就違背了「開發——封閉」原則。
A一個系統不要求依賴產品類實例如何被建立、組合和表達的表達,這點也是全部工廠模式應用的前提。
B這個系統有多個系列產品,而系統中只消費其中某一系列產品
C系統要求提供一個產品類的庫,全部產品以一樣的接口出現,客戶端不須要依賴具體實現。
抽象工廠模式在實際中的應用也是至關頻繁的,然而在咱們.NET類庫中也存在應用抽象工廠模式的類,這個類就是System.Data.Common.DbProviderFactory,這個類位於System.Data.dll程序集中,該類扮演抽象工廠模式中抽象工廠的角色
public sealed class SqlClientFactory : DbProviderFactory, IServiceProvider
在軟件系統中,有時須要建立一個複雜對象,而且這個複雜對象由其各部分子對象經過必定的步驟組合而成。例如一個採購系統中,若是須要採購員去採購一批電腦時,在這個實際需求中,電腦就是一個複雜的對象,它是由CPU、主板、硬盤、顯卡、機箱等組裝而成的,若是此時讓採購員一臺一臺電腦去組裝的話真是要累死採購員了。這裏就能夠採用建造者模式來解決這個問題,咱們能夠把電腦的各個組件的組裝過程封裝到一個建造者類對象裏,建造者只要負責返還給客戶端所有組件都建造完畢的產品對象就能夠了。然而現實生活中也是如此的,若是公司要採購一批電腦,此時採購員不可能本身去買各個組件並把它們組織起來,此時採購員只須要像電腦城的老闆說本身要採購什麼樣的電腦就能夠了,電腦城老闆天然會把組裝好的電腦送到公司。下面就以這個例子來展開建造者模式的介紹。
建造者模式指的是將一個產品的內部表示與產品的構造過程分割開來,從而可使一個建造過程生成具體不一樣的內部表示的產品對象。強調的是產品的構造過程。其實現要點有:將產品的內部表示與產品的構造過程分割開來。問:如何把它們分割開呢?答:不要把產品的構造過程放在產品類中,而是由建造者類來負責構造過程,產品的內部表示放在產品類中,這樣不就分割開了嘛。
具體的UML結構圖以下所示:
/// <summary> /// 以組裝電腦爲例子 /// 每臺電腦的組成過程都是一致的,可是使用一樣的構建過程能夠建立不一樣的表示(便可以組裝成不同的電腦,配置不同) /// 組裝電腦的這個場景就能夠應用建造者模式來設計 /// </summary> namespace 設計模式之建造者模式 { /// <summary> /// 客戶類 /// </summary> class Customer { static void Main(string[] args) { // 客戶找到電腦城老闆說要買電腦,這裏要裝兩臺電腦 // 建立指揮者和構造者 Director director = new Director(); Builder b1 = new ConcreteBuilder1(); Builder b2 = new ConcreteBuilder2(); // 老闆叫員工去組裝第一臺電腦 director.Construct(b1); // 組裝完,組裝人員搬來組裝好的電腦 Computer computer1 = b1.GetComputer(); computer1.Show(); // 老闆叫員工去組裝第二臺電腦 director.Construct(b2); Computer computer2 = b2.GetComputer(); computer2.Show(); Console.Read(); } } /// <summary> /// 小王和小李難道會自願地去組裝嘛,誰不想休息的,這必須有一我的叫他們去組裝纔會去的 /// 這我的固然就是老闆了,也就是建造者模式中的指揮者 /// 指揮建立過程類 /// </summary> public class Director { // 組裝電腦 public void Construct(Builder builder) { builder.BuildPartCPU(); builder.BuildPartMainBoard(); } } /// <summary> /// 電腦類 /// </summary> public class Computer { // 電腦組件集合 private IList<string> parts = new List<string>(); // 把單個組件添加到電腦組件集合中 public void Add(string part) { parts.Add(part); } public void Show() { Console.WriteLine("電腦開始在組裝......."); foreach (string part in parts) { Console.WriteLine("組件"+part+"已裝好"); } Console.WriteLine("電腦組裝好了"); } } /// <summary> /// 抽象建造者,這個場景下爲 "組裝人" ,這裏也能夠定義爲接口 /// </summary> public abstract class Builder { // 裝CPU public abstract void BuildPartCPU(); // 裝主板 public abstract void BuildPartMainBoard(); // 固然還有裝硬盤,電源等組件,這裏省略 // 得到組裝好的電腦 public abstract Computer GetComputer(); } /// <summary> /// 具體建立者,具體的某我的爲具體建立者,例如:裝機小王啊 /// </summary> public class ConcreteBuilder1 : Builder { Computer computer = new Computer(); public override void BuildPartCPU() { computer.Add("CPU1"); } public override void BuildPartMainBoard() { computer.Add("Main board1"); } public override Computer GetComputer() { return computer; } } /// <summary> /// 具體建立者,具體的某我的爲具體建立者,例如:裝機小李啊 /// 又裝另外一臺電腦了 /// </summary> public class ConcreteBuilder2 : Builder { Computer computer = new Computer(); public override void BuildPartCPU() { computer.Add("CPU2"); } public override void BuildPartMainBoard() { computer.Add("Main board2"); } public override Computer GetComputer() { return computer; } } }
在建造者模式中,指揮者是直接與客戶端打交道的,指揮者將客戶端建立產品的請求劃分爲對各個部件的建造請求,再將這些請求委派到具體建造者角色,具體建造者角色是完成具體產品的構建工做的,卻不爲客戶所知道。
建造者模式主要用於「分步驟來構建一個複雜的對象」,其中「分步驟」是一個固定的組合過程,而複雜對象的各個部分是常常變化的(也就是說電腦的內部組件是常常變化的,這裏指的的變化如硬盤的大小變了,CPU由單核變雙核等)。
產品不須要抽象類,因爲建造模式的建立出來的最終產品可能差別很大,因此不大可能提煉出一個抽象產品類。
在前面文章中介紹的抽象工廠模式解決了「系列產品」的需求變化,而建造者模式解決的是 「產品部分」 的須要變化。
因爲建造者隱藏了具體產品的組裝過程,因此要改變一個產品的內部表示,只須要再實現一個具體的建造者就能夠了,從而能很好地應對產品組成組件的需求變化。
在.NET 類庫中,System.Text.StringBuilder(存在mscorlib.dll程序集中)就是一個建造者模式的實現。不過它的實現屬於建造者模式的演化,此時的建造者模式沒有指揮者角色和抽象建造者角色,StringBuilder類即扮演着具體建造者的角色,也同時扮演了指揮者和抽象建造者的角色
原型模式指的是經過給出一個原型對象來指明所要建立的對象類型,而後用複製的方法來建立出更多的同類型對象。其實現要點有:給出一個原型對象。問:如何辦到呢?答:很簡單嘛,直接給出一個原型類就行了。經過複製的方法來建立同類型對象。問:又是如何實現呢?答:.NET能夠直接調用MemberwiseClone方法來實現淺拷貝
具體的UML結構圖以下所示:
原型模式用一個原型對象來指明所要建立的對象類型,而後用複製這個原型對象的方法來建立出更多的同類型對象,它與工廠方法模式的實現很是類似,其中原型模式中的Clone方法就相似工廠方法模式中的工廠方法,只是工廠方法模式的工廠方法是經過new運算符從新建立一個新的對象(至關於原型模式的深拷貝實現),而原型模式是經過調用MemberwiseClone方法來對原來對象進行拷貝,也就是複製
///火影忍者中鳴人的影分身和孫悟空的的變都是原型模式 class Client { static void Main(string[] args) { // 孫悟空 原型 MonkeyKingPrototype prototypeMonkeyKing = new ConcretePrototype("MonkeyKing"); // 變一個 MonkeyKingPrototype cloneMonkeyKing = prototypeMonkeyKing.Clone() as retePrototype; Console.WriteLine("Cloned1:\t"+cloneMonkeyKing.Id); // 變兩個 MonkeyKingPrototype cloneMonkeyKing2 = prototypeMonkeyKing.Clone() as retePrototype; Console.WriteLine("Cloned2:\t" + cloneMonkeyKing2.Id); Console.ReadLine(); } } /// <summary> /// 孫悟空原型 /// </summary> public abstract class MonkeyKingPrototype { public string Id { get; set; } public MonkeyKingPrototype(string id) { this.Id = id; } // 克隆方法,即孫大聖說「變」 public abstract MonkeyKingPrototype Clone(); } /// <summary> /// 建立具體原型 /// </summary> public class ConcretePrototype : MonkeyKingPrototype { public ConcretePrototype(string id) : base(id) { } /// <summary> /// 淺拷貝 /// </summary> /// <returns></returns> public override MonkeyKingPrototype Clone() { // 調用MemberwiseClone方法實現的是淺拷貝,另外還有深拷貝 return (MonkeyKingPrototype)this.MemberwiseClone(); //MemberwiseClone是object類型自有的一個方法 } }
原型模式的優勢有:
A原型模式向客戶隱藏了建立新實例的複雜性
B原型模式容許動態增長或較少產品類。
C原型模式簡化了實例的建立結構,工廠方法模式須要有一個與產品類等級結構相同的等級結構,而原型模式不須要這樣。
D產品類不須要事先肯定產品的等級結構,由於原型模式適用於任何的等級結構
原型模式的缺點有:
A每一個類必須配備一個克隆方法
B配備克隆方法須要對類的功能進行通盤考慮,這對於全新的類不是很難,但對於已有的類不必定很容易,特別當一個類引用不支持串行化的間接對象,或者引用含有循環結構的時候。
在.NET中能夠很容易地經過實現ICloneable接口(這個接口就是原型,提供克隆方法,至關於與上面代碼中MonkeyKingPrototype抽象類)中Clone()方法來實現原型模式,若是咱們想咱們自定義的類具備克隆的功能,首先定義類繼承與ICloneable接口並實現Clone方法。在.NET中實現了原型模式的類以下圖所示(圖中只截取了部分,能夠用Reflector反編譯工具進行查看):