在軟件系統中,有時須要建立一個複雜對象,而且這個複雜對象由其各部分子對象經過必定的步驟組合而成。例如一個採購系統中,若是須要採購員去採購一批電腦時,在這個實際需求中,電腦就是一個複雜的對象,它是由CPU、主板、硬盤、顯卡、機箱等組裝而成的,若是此時讓採購員一臺一臺電腦去組裝的話真是要累死採購員了,這裏就能夠採用建造者模式來解決這個問題,咱們能夠把電腦的各個組件的組裝過程封裝到一個建造者類對象裏,建造者只要負責返還給客戶端所有組件都建造完畢的產品對象就能夠了。然而現實生活中也是如此的,若是公司要採購一批電腦,此時採購員不可能本身去買各個組件並把它們組織起來,此時採購員只須要像電腦城的老闆說本身要採購什麼樣的電腦就能夠了,電腦城老闆天然會把組裝好的電腦送到公司。下面就以這個例子來展開建造者模式的介紹。c#
在這個例子中,電腦城的老闆是直接與客戶(也就是指採購員)聯繫的,然而電腦的組裝是由老闆指揮裝機人員去把電腦的各個部件組裝起來,真真負責建立產品(這裏產品指的就是電腦)的人就是電腦城的裝機人員。理清了這個邏輯過程以後,下面就具體看下如何用代碼來表示這種現實生活中的邏輯過程:設計模式
using System; using System.Collections.Generic; using System.Linq; using System.Text; /// <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; } } }
上面代碼中都有詳細的註釋代碼,這裏就不過多解釋,你們能夠參考代碼和註釋來與現實生活中的例子作對比,下圖展現了上面代碼的運行結果:ide
介紹完了建造者模式的具體實現以後嗎,下面具體看下建造者模式的具體定義是怎樣的。ui
建造者模式(Builder Pattern):將一個複雜對象的構建於它的表示分離,使得一樣的構建過程能夠建立不一樣的表示。spa
建造者模式使得建造代碼與表示代碼的分離,可使客戶端沒必要知道產品內部組成的細節,從而下降了客戶端與具體產品之間的耦合度,下面經過類圖來幫助你們更好地理清建造者模式中類之間的關係。設計
介紹完了建造者模式的具體實現以後,讓咱們總結下建造模式的實現要點:對象
在建造者模式中,指揮者是直接與客戶端打交道的,指揮者將客戶端建立產品的請求劃分爲對各個部件的建造請求,再將這些請求委派到具體建造者角色,具體建造者角色是完成具體產品的構建工做的,卻不爲客戶所知道。接口
建造者模式主要用於「分步驟來構建一個複雜的對象」,其中「分步驟」是一個固定的組合過程,而複雜對象的各個部分是常常變化的(也就是說電腦的內部組件是常常變化的,這裏指的的變化如硬盤的大小變了,CPU由單核變雙核等)。字符串
產品不須要抽象類,因爲建造模式的建立出來的最終產品可能差別很大,因此不大可能提煉出一個抽象產品類。string
在前面文章中介紹的抽象工廠模式解決了「系列產品」的需求變化,而建造者模式解決的是 「產品部分」 的須要變化。
因爲建造者隱藏了具體產品的組裝過程,因此要改變一個產品的內部表示,只須要再實現一個具體的建造者就能夠了,從而能很好地應對產品組成組件的需求變化。
前面的設計模式在.NET類庫中都有相應的實現,那在.NET 類庫中,是否也存在建造者模式的實現呢? 然而對於疑問的答案是確定的,在.NET 類庫中,System.Text.StringBuilder(存在mscorlib.dll程序集中)就是一個建造者模式的實現。不過它的實現屬於建造者模式的演化,此時的建造者模式沒有指揮者角色和抽象建造者角色,StringBuilder類即扮演着具體建造者的角色,也同時扮演了指揮者和抽象建造者的角色,此時建造模式的實現以下:
/// <summary> /// 建造者模式的演變 /// 省略了指揮者角色和抽象建造者角色 /// 此時具體建造者角色扮演了指揮者和建造者兩個角色 /// </summary> public class Builder { // 具體建造者角色的代碼 private Product product = new Product(); public void BuildPartA() { product.Add("PartA"); } public void BuildPartB() { product.Add("PartB"); } public Product GetProduct() { return product; } // 指揮者角色的代碼 public void Construct() { BuildPartA(); BuildPartB(); } } /// <summary> /// 產品類 /// </summary> public class Product { // 產品組件集合 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("產品組裝完成"); } } // 此時客戶端也要作相應調整 class Client { private static Builder builder; static void Main(string[] args) { builder = new Builder(); builder.Construct(); Product product = builder.GetProduct(); product.Show(); Console.Read(); } }
StringBuilder類扮演着建造string對象的具體建造者角色,其中的ToString()方法用來返回具體產品給客戶端(至關於上面代碼中GetProduct方法)。其中Append方法用來建立產品的組件(至關於上面代碼中BuildPartA和BuildPartB方法),由於string對象中每一個組件都是字符,因此也就不須要指揮者的角色的代碼(指的是Construct方法,用來調用建立每一個組件的方法來完成整個產品的組裝),由於string字符串對象中每一個組件都是同樣的,都是字符,因此Append方法也充當了指揮者Construct方法的做用。
到這裏,建造者模式的介紹就結束了,建造者模式(Builder Pattern),將一個複雜對象的構建與它的表示分離,使的一樣的構建過程能夠建立不一樣的表示。建造者模式的本質是使組裝過程(用指揮者類進行封裝,從而達到解耦的目的)和建立具體產品解耦,使咱們不用去關心每一個組件是如何組裝的。