前面學習了簡單工廠模式,工廠方法模式以及抽象工廠模式,這些都是創建類的對象所使用的一些常用的方法和套路, 那麼如果我們創建一個很複雜的對象可上面的三種方法都不太適合,那麼“專業的事交給專業人去做”,23設計模式總有一個模式是適合這種複雜對象的創建。比如現在的智能手機組成, 它包括一個屏幕,攝像頭,耳 ...
前面學習了簡單工廠模式,工廠方法模式以及抽象工廠模式,這些都是創建類的對象所使用的一些常用的方法和套路, 那麼如果我們創建一個很複雜的對象可上面的三種方法都不太適合,那麼“專業的事交給專業人去做”,23設計模式總有一個模式是適合這種複雜對象的創建。比如現在的智能手機組成, 它包括一個屏幕,攝像頭,耳機介面,USB介面,CPU, RAM,主板等等, 但是每一個型號的手機的屏幕又不一樣,有的是劉海的,有的是全屏的,有的是全面屏的,CUP 也不一樣,有驍龍820 的,有 660的還有麒麟920 的等等,手機的組成圖如下:
那麼要創建一個這樣的複雜對象, 該怎麼創建呢? 那麼該建造者模式閃亮登場了。
一、建造者模式定義
建造者模式(Builder Pattern):將一個複雜對象的構建與它的表示分離,使得同樣的構建過程可以創建不同的表示。建造者模式是一種對象創建型模式。
二、建造者模式結構圖
1、AbstractBuilder(抽象建造者):
它為創建一個產品Product對象的各個部件指定抽象介面,在該介面中一般聲明兩類方法,一類方法是buildPartX(),它們用於創建複雜對象的各個部件;另一類方法是GetResult(),它們用於返回覆雜對象。AbstractBuilder既可以是抽象類,也可以是介面。
2、ConcreteBuilder(具體建造者):
它實現了AbstractBuilder介面,實現各個部件的具體構造和裝配方法,定義並明確它所創建的複雜對象,也可以提供一個方法返回創建好的複雜產品對象。
3、Product(產品角色):
它是被構建的複雜對象,包含多個組成部件,具體建造者創建該產品的內部表示並定義它的裝配過程。
4、Director(導演):
負責安排複雜對象的建造次序,導演與抽象建造者之間存在關聯關係,可以在其Construct()建造方法中調用建造者對象的部件構造與裝配方法,完成複雜對象的建造。客戶端一般只需要與指揮者進行交互,在客戶端確定具體建造者的類型,並實例化具體建造者對象(也可以通過配置文件和反射機制),然後通過指揮者類的構造函數將該對象傳入指揮者類中。
三、建造者模式典型代碼結構
public class Product { public string PartA { get; set; } public string PartB { get; set; } public string PartC { get; set; } } public abstract class AbstractBuilder { protected Product product = new Product(); public abstract void BuildPartA(); public abstract void BuildPartB(); public abstract void BuildPartC(); public Product GetResult() { return product; } } public class ConreteBuilder : AbstractBuilder { public override void BuildPartA() { this.product.PartA = "PartA"; } public override void BuildPartB() { this.product.PartB = "PartB"; } public override void BuildPartC() { this.product.PartC = "PartC"; } } public class Director { private AbstractBuilder builder; public Director(AbstractBuilder builder) { this.builder = builder; } public Product Construct() { builder.BuildPartA(); builder.BuildPartB(); builder.BuildPartC(); return builder.GetResult(); } }
客戶端調用:
static void Main(string[] args) { AbstractBuilder builder = new ConreteBuilder(); Director director = new Director(builder); Product product = director.Construct(); Console.WriteLine(product.PartA); Console.WriteLine(product.PartB); Console.WriteLine(product.PartC); Console.ReadKey(); }
輸出結果:
四、建造者模式實例
我們用本文開頭提出的手組成的例子來挑選幾個核心部件來構造幾個型號的手機,使用建造者模式。
1、X1型手機:CUP 驍龍 835, RAM 6GB ,屏幕 劉海全屏, 硬碟 64GB。
2、X2 型手機: CUP 麒麟 930, RAM 8G, 屏幕 全面屏, 硬碟 128GB。
3、X3型手機: CPU 驍龍 960 RAM 10G,屏幕 超清全面屏 256GB。
UML 圖如下:
代碼:
public class Mobile { public string CPU { get; set; } public string Screen { get; set; } public string RAM { get; set; } public string Disk { get; set; } } public abstract class MobileBuilder { protected Mobile mobile = new Mobile(); public abstract void BuildCPU(); public abstract void BuildScreen(); public abstract void BuildRAM(); public abstract void BuildDisk(); public Mobile GetMobile() { return this.mobile; } } public class X1Builder:MobileBuilder { public override void BuildCPU() { mobile.CPU = "[X1]:CPU had been built."; } public override void BuildScreen() { mobile.Screen = "[X1]:Screen had been built."; } public override void BuildRAM() { mobile.RAM = "[X1]:RAM had been built."; } public override void BuildDisk() { mobile.Disk = "[X1]:Disk had been built."; } } public class X2Builder:MobileBuilder { public override void BuildCPU() { mobile.CPU = "[X2]:CPU had been built."; } public override void BuildScreen() { mobile.Screen = "[X2]:Screen had been built."; } public override void BuildRAM() { mobile.RAM = "[X2]:RAM had been built."; } public override void BuildDisk() { mobile.Disk = "[X2]:Disk had been built."; } } public class X3Builder:MobileBuilder { public override void BuildCPU() { mobile.CPU = "[X3]:CPU had been built."; } public override void BuildScreen() { mobile.Screen = "[X3]:Screen had been built."; } public override void BuildRAM() { mobile.RAM = "[X3]:RAM had been built."; } public override void BuildDisk() { mobile.Disk = "[X3]:Disk had been built."; } } public class MobileDirector { private MobileBuilder builder; public MobileDirector(MobileBuilder builder) { this.builder=builder; } public Mobile GetMobile(){ this.builder.BuildCPU(); this.builder.BuildRAM(); this.builder.BuildScreen(); this.builder.BuildDisk(); return this.builder.GetMobile(); } }
調用代碼如下:
static void Main(string[] args) { MobileBuilder builder=new X1Builder(); MobileDirector mobileDrector = new MobileDirector(builder); Mobile mobile = mobileDrector.GetMobile(); Console.WriteLine(mobile.CPU); Console.WriteLine(mobile.Screen); Console.WriteLine(mobile.RAM); Console.WriteLine(mobile.Disk); Console.ReadKey(); }
輸出結果:
如果想生產X2型號的手機,只需要將具體建造者代碼修改一下就可以了,即將下麵的一行代碼:
MobileBuilder builder=new X1Builder();
改成:
MobileBuilder builder=new X2Builder();
輸出結果:
也可以將具體建造者類配置在配置文件中,通過反射來創建建造者對象進而創建出新的型號的手機。
在配置文件中加入如下配置:
<appSettings> <add key="Builder" value="DesignPattern.Builder.MobileInstance.X3Builder"/> </appSettings>
客戶端調用代碼如下:
static void Main(string[] args) { MobileBuilder builder; var setting = ConfigurationSettings.AppSettings["Builder"]; var obj = Type.GetType(setting); if (obj == null) return; builder = Activator.CreateInstance(obj) as MobileBuilder; if (builder == null) return; MobileDirector mobileDrector = new MobileDirector(builder); Mobile mobile = mobileDrector.GetMobile(); Console.WriteLine(mobile.CPU); Console.WriteLine(mobile.Screen); Console.WriteLine(mobile.RAM); Console.WriteLine(mobile.Disk); Console.ReadKey(); }
輸出結果:
五、建造者模式的優點
- 在建造者模式中,客戶端不必知道產品內部組成的細節,將產品本身與產品的創建過程解耦,使得相同的創建過程可以創建不同的產品對象。
- 每一個具體建造者都相對獨立,而與其他的具體建造者無關,因此可以很方便地替換具體建造者或增加新的具體建造者,用戶使用不同的具體建造者即可得到不同的產品對象。由於指揮者類針對抽象建造者編程,增加新的具體建造者無須修改原有類庫的代碼,系統擴展方便,符合“開閉原則(OCP)“
- 可以更加精細地控制產品的創建過程。將複雜產品的創建步驟分解在不同的方法中,使得創建過程更加清晰,也更方便使用程式來控制創建過程
六、建造者模式的缺點
- 建造者模式所創建的產品一般具有較多的共同點,其組成部分相似,如果產品之間的差異性很大,例如很多組成部分都不相同,不適合使用建造者模式,因此其使用範圍受到一定的限制。
- 如果產品的內部變化複雜,可能會導致需要定義很多具體建造者類來實現這種變化,導致系統變得很龐大,增加系統的理解難度和運行成本。
七、建造者模式的使用場景
- 需要生成的產品對象有複雜的內部結構,這些產品對象通常包含多個成員屬性。
- 需要生成的產品對象的屬性相互依賴,需要指定其生成順序。
- 對象的創建過程獨立於創建該對象的類。在建造者模式中通過引入了指揮者類,將創建過程封裝在指揮者類中,而不在建造者類和客戶類中。
- 隔離複雜對象的創建和使用,並使得相同的創建過程可以創建不同的產品。
八、擴展
Director在建造者模式中扮演著重要的角色,Director類看似簡單但是作用卻非常大,它決定複雜對象各個部分的創建順序並且將構建對象的過程和具體建造者隔離,比如創建一個房子,首先肯定要做的事情是打地基,然後是磊牆,然後是封頂,最後是裝修,這個過程是有順序的且必須是這個順序,其它過程不無法完成房子的建造。
Director'就好像是拍電影導演一樣,導演要拍一部電影,需要拍攝若幹影像片段,最後由剪輯師做剪接拼接,導演最後會進行最終的剪輯排版合成一個長片-- 電影。那麼電影就充當了建造者模式中的複雜產品,拍攝的影像片段就是產片的各個部分,剪輯就是建造者模式的具體具體建造者,劇本是建造者模式的抽象建造者,導演就是Director。那麼在在這個過程中,編劇是不可以兼任導演? 剪輯師是不是也可以兼任導演呢?答案是肯定的。
1.抽象建造者幹了Director的活
那上面手機建造的實例,刪除掉Diretor, 將創建複雜對象的邏輯放到抽象建造者中,並且使子類方法不能重寫父類中的建造產品的方法,引用之前的配置代碼演變成如下:
public class Mobile { public string CPU { get; set; } public string Screen { get; set; } public string RAM { get; set; } public string Disk { get; set; } } public abstract class MobileBuilder { protected Mobile mobile = new Mobile(); public abstract void BuildCPU(); public abstract void BuildScreen(); public abstract void BuildRAM(); public abstract void BuildDisk(); public static Mobile GetMobile(MobileBuilder builder) { builder.BuildCPU(); builder.BuildScreen(); builder.BuildRAM(); builder.BuildDisk(); return builder.mobile; } } public class X1Builder : MobileBuilder { public override void BuildCPU() { mobile.CPU = "[X1]:CPU had been built."; } public override void BuildScreen() { mobile.Screen = "[X1]:Screen had been built."; } public override void BuildRAM() { mobile.RAM = "[X1]:RAM had been built."; } public override void BuildDisk() { mobile.Disk = "[X1]:Disk had been built."; } } public class X2Builder : MobileBuilder { public override void BuildCPU() { mobile.CPU = "[X2]:CPU had been built."; } public override void BuildScreen() { mobile.Screen = "[X2]:Screen had been built."; } public override void BuildRAM() { mobile.RAM = "[X2]:RAM had been built."; } public override void BuildDisk() { mobile.Disk = "[X2]:Disk had been built."; } } public class X3Builder : MobileBuilder { public override void BuildCPU() { mobile.CPU = "[X3]:CPU had been built."; } public override void BuildScreen() { mobile.Screen = "[X3]:Screen had been built."; } public override void BuildRAM() { mobile.RAM = "[X3]:RAM had been built."; } public override void BuildDisk() { mobile.Disk = "[X3]:Disk had been built."; } }
調用代碼:
static void Main(string[] args) { MobileBuilder builder; var setting = ConfigurationSettings.AppSettings["Builder"]; var obj = Type.GetType(setting); if (obj == null) return; builder = Activator.CreateInstance(obj) as MobileBuilder; if (builder == null) return; Mobile mobile = builder.GetMobile(); Console.WriteLine(mobile.CPU); Console.WriteLine(mobile.Screen); Console.WriteLine(mobile.RAM); Console.WriteLine(mobile.Disk); Console.ReadKey(); }
輸出結果:
這種在抽象建造者中使用了一個靜態方法來創建產品的做法的好處是產品創建出來的一致性很好,創建產品流程被統一封裝,一般不會有差異,這種方式抽象類控制了產品建造的順序,並且所有的產品的創建順序都不能改變了(如造房子的流程),對於要求創建順序一致,並且產品部件的創建都路程一致的產品來說這是一個優點。
但是如果想創建出來的產品有差異,每一個產品的順序都不一樣那該怎麼辦呢?比如現在這種方法創建出來的順序是: CPU=》Screen=》RAM=》Disk, 那麼我要想X3型號的手機創建順序變成:CPU=》RAM=》Disk=》Screen。 這種方法就顯得不靈活了,沒有辦法做到個性化了。處理典型的將建造過程的控制權交給Director外,還可以不用Director來完成嗎?
2、具體建造者和抽象建造者都可以乾Director的活
這裡我們依然刪掉Director, 將抽象方法中的創建方法變成子類可以重寫的虛方法就可以了,然後在具體建造者中重寫抽象建造者的創建方法就可以了。
現在我們創建X1,X2,X3型號的手機的順序分別是這樣的:
X1: CPU=》Screen=》RAM=》Disk
X2:CPU=》RAM=》Disk=》Screen
X3:CPU=》Disk=》RAM=》Screen
代碼如下:
public class Mobile { public string CPU { get; set; } public string Screen { get; set; } public string RAM { get; set; } public string Disk { get; set; } } public abstract class MobileBuilder { protected Mobile mobile = new Mobile(); public abstract void BuildCPU(); public abstract void BuildScreen(); public abstract void BuildRAM(); public abstract void BuildDisk(); public virtual Mobile GetMobile() { this.BuildCPU(); this.BuildScreen(); this.BuildRAM(); this.BuildDisk(); Console.WriteLine(mobile.CPU); Console.WriteLine(mobile.Screen); Console.WriteLine(mobile.RAM); Console.WriteLine(mobile.Disk); return this.mobile; } } public class X1Builder : MobileBuilder { public override void BuildCPU() { mobile.CPU = "[X1]:CPU had been built."; } public override void BuildScreen() { mobile.Screen = "[X1]:Screen had been built."; } public override void BuildRAM() { mobile.RAM = "[X1]:RAM had been built."; } public override void BuildDisk() { mobile.Disk = "[X1]:Disk had been built."; } } public class X2Builder : MobileBuilder { public override void BuildCPU() { mobile.CPU = "[X2]:CPU had been built."; } public override void BuildScreen() { mobile.Screen = "[X2]:Screen had been built."; } public override void BuildRAM() { mobile.RAM = "[X2]:RAM had been built."; } public override void BuildDisk() { mobile.Disk = "[X2]:Disk had been built."; } public override Mobile GetMobile() { BuildCPU(); BuildRAM(); BuildDisk(); BuildScreen(); Console.WriteLine(mobile.CPU); Console.WriteLine(mobile.RAM); Console.WriteLine(mobile.Disk); Console.WriteLine(mobile.Screen); return this.mobile; } } public class X3Builder : MobileBuilder { public override void BuildCPU() { mobile.CPU = "[X3]:CPU had been built."; } public override void BuildScreen() { mobile.Screen = "[X3]:Screen had been built."; } public override void BuildRAM() { mobile.RAM = "[X3]:RAM had been built."; } public override void BuildDisk() { mobile.Disk = "[X3]:Disk had been built."; } public override Mobile GetMobile() { BuildCPU(); BuildDisk(); BuildRAM(); BuildScreen(); Console.WriteLine(mobile.CPU); Console.WriteLine(mobile.Disk); Console.WriteLine(mobile.RAM); Console.WriteLine(mobile.Screen); return this.mobile; } }
調用代碼:
static void Main(string[] args) { MobileBuilder builder; var setting = ConfigurationSettings.AppSettings["Builder"]; var obj = Type.GetType(setting); if (obj == null) return; builder = Activator.CreateInstance(obj) as MobileBuilder; if (builder == null) return; Mobile mobile = builder.GetMobile(); Console.ReadKey(); }
輸出結果:
3. 控制某些部件不用被創建
假如要造一個X4型號的手機,這個手機支持NFC,我們知道X1,X2,X3中都不支持NFC,那怎麼辦呢?,我們可以給抽象建造者類加一個方法,叫 HasNFC()並且返回bool值,並將其設置成預設值為false。 修改抽象建造者的GetMobile() 方法,只有當HasNFC()返回 true是才創建NFC模塊,並且在X4Builder的具體建造者類中重寫HasNFC()方法使其返回true就可以了。
代碼如下:
public class Mobile { public string CPU { get; set; } public string Screen { get; set; } public string RAM { get; set; } public string Disk { get; set; } } public abstract class MobileBuilder { protected Mobile mobile = new Mobile(); public abstract void BuildCPU(); public abstract void BuildScreen(); public abstract void BuildRAM(); public abstract void BuildDisk(); public virtual void BuildNFC() { Console.WriteLine("NFC had been built."); } protected virtual bool HasNFC() { return false; } public virtual Mobile GetMobile() { this.BuildCPU(); this.BuildScreen(); this.BuildRAM(); this.BuildDisk(); if(HasNFC()) { this.BuildNFC(); } Console.WriteLine(mobile.CPU); Console.WriteLine(mobile.Screen); Console.WriteLine(mobile.RAM); Console.WriteLine(mobile.Disk); return this.mobile; } } public class X1Builder : MobileBuilder { public override void BuildCPU() { mobile.CPU = "[X1]:CPU had been built."; } public override void BuildScreen() { mobile.Screen = "[X1]:Screen had been built."; } public override void BuildRAM() { mobile.RAM = "[X1]:RAM had been built."; } public override void BuildDisk() { mobile.Disk = "[X1]:Disk had been built."; } } public class X2Builder : MobileBuilder { public override void BuildCPU() { mobile.CPU = "[X2]:CPU had been built."; } public override void BuildScreen() { mobile.Screen = "[X2]:Screen had been built."; } public override void BuildRAM() { mobile.RAM = "[X2]:RAM had been built."; } public override void BuildDisk() { mobile.Disk = "[X2]:Disk had been built."; } public override Mobile GetMobile() { BuildCPU(); BuildRAM(); BuildDisk(); BuildScreen(); Console.WriteLine(mobile.CPU); Console.WriteLine(mobile.RAM); Console.WriteLine(mobile.Disk); Console.WriteLine(mobile.Screen); return this.mobile; } } public class X3Builder : MobileBuilder { public override void BuildCPU() { mobile.CPU = "[X3]:CPU had been built."; } public override void BuildScreen() { mobile.Screen = "[X3]:Screen had been built."; } public override void BuildRAM() { mobile.RAM = "[X3]:RAM had been built."; } public override void BuildDisk() { mobile.Disk = "[X3]:Disk had been built."; } public override Mobile GetMobile() { BuildCPU(); BuildDisk(); BuildRAM(); BuildScreen(); Console.WriteLine(mobile.CPU); Console.WriteLine(mobile.Disk); Console.WriteLine(mobile.RAM); Console.WriteLine(mobile.Screen); return this.mobile; } } public class X4Builder : MobileBuilder { public override void BuildCPU() { mobile.CPU = "[X4]:CPU had been built."; } public override void BuildScreen() { mobile.Screen = "[X4]:Screen had been built."; } public override void BuildRAM() { mobile.RAM = "[X4]:RAM had been built."; } public override void BuildDisk() { mobile.Disk = "[X4]:Disk had been built."; } protected override bool HasNFC() { return true; } }
App.Config 配置:
<appSettings> <add key="Builder" value="DesignPattern.Builder.MobileInstance.X3Builder"/> </appSettings>
客戶端代碼:
static void Main(string[] args) { MobileBuilder builder; var setting = ConfigurationSettings.AppSettings["Builder"]; var obj = Type.GetType(setting); if (obj == null) return; builder = Activator.CreateInstance(obj) as MobileBuilder; if (builder == null) return; Mobile mobile = builder.GetMobile(); Console.ReadKey(); }
輸出結果:
修改配置文件,使其造一臺X4 如下,調用代碼不變:
<appSettings> <add key="Builder" value="DesignPattern.Builder.MobileInstance.X4Builder"/> </appSettings>
輸出結果:
好了建造者模式就探討到這裡。