沒有人買車會只買一個輪胎或者方向盤,大家買的都是一輛包含輪胎、方向盤和發動機等多個部件的完整汽車。如何將這些部件組裝成一輛完整的汽車並返回給用戶,這是建造者模式需要解決的問題。建造者模式又稱爲生成器模式,它是一種較爲複雜、使用頻率也相對較低的創建型模式。建造者模式爲客戶端返回的不是一個簡單的產品,而是一個由多個部件組成的複雜產品。大概瞭解一下這個模型就行。
一、建造者模式概述
建造者模式是較爲複雜的創建型模式,它將客戶端與包含多個組成部分(或部件)的複雜對象的創建過程分離,客戶端無須知道複雜對象的內部組成部分與裝配方式,只需要知道所需建造者的類型即可。它關注如何一步一步創建一個的複雜對象,不同的具體建造者定義了不同的創建過程,且具體建造者相互獨立,增加新的建造者非常方便,無須修改已有代碼,系統具有較好的擴展性。
二、建造者模式定義
建造者模式(Builder Pattern):將一個複雜對象的構建與它的表示分離,使得同樣的構建過程可以創建不同的表示。建造者模式是一種對象創建型模式。
建造者模式一步一步創建一個複雜的對象,它允許用戶只通過指定複雜對象的類型和內容就可以構建它們,用戶不需要知道內部的具體構建細節。建造者模式結構如圖所示:
三、建造者模式角色:
- Builder(抽象建造者):它爲創建一個產品Product對象的各個部件指定抽象接口,在該接口中一般聲明兩類方法,一類方法是buildPartX(),它們用於創建複雜對象的各個部件;另一類方法是getResult(),它們用於返回複雜對象。Builder既可以是抽象類,也可以是接口。
- ConcreteBuilder(具體建造者):它實現了Builder接口,實現各個部件的具體構造和裝配方法,定義並明確它所創建的複雜對象,也可以提供一個方法返回創建好的複雜產品對象。
- Product(產品角色):它是被構建的複雜對象,包含多個組成部件,具體建造者創建該產品的內部表示並定義它的裝配過程。
- Director(指揮者):指揮者又稱爲導演類,它負責安排複雜對象的建造次序,指揮者與抽象建造者之間存在關聯關係,可以在其construct()建造方法中調用建造者對象的部件構造與裝配方法,完成複雜對象的建造。客戶端一般只需要與指揮者進行交互,在客戶端確定具體建造者的類型,並實例化具體建造者對象(也可以通過配置文件和反射機制),然後通過指揮者類的構造函數或者Setter方法將該對象傳入指揮者類中。
四、建造者模式代碼示例
在建造者模式的定義中提到了複雜對象,那麼什麼是複雜對象?簡單來說,複雜對象是指那些包含多個成員屬性的對象,這些成員屬性也稱爲部件或零件,如汽車包括方向盤、發動機、輪胎等部件,電子郵件包括發件人、收件人、主題、內容、附件等部件,一個典型的複雜對象類代碼示例如下:
class Product {
private String partA; //定義部件,部件可以是任意類型,包括值類型和引用類型
private String partB;
private String partC;
//partA的Getter方法和Setter方法省略
//partB的Getter方法和Setter方法省略
//partC的Getter方法和Setter方法省略
}
在抽象建造者類中定義了產品的創建方法和返回方法,其典型代碼如下:
abstract class Builder {
//創建產品對象
protected Product product=new Product();
public abstract void buildPartA();
public abstract void buildPartB();
public abstract void buildPartC();
//返回產品對象
public Product getResult() {
return product;
}
}
在抽象類Builder中聲明瞭一系列抽象的buildPartX()方法用於創建複雜產品的各個部件,具體建造過程在ConcreteBuilder中實現,此外還提供了工廠方法getResult(),用於返回一個建造好的完整產品。
在ConcreteBuilder中實現了buildPartX()方法,通過調用Product的setPartX()方法可以給產品對象的成員屬性設值。不同的具體建造者在實現buildPartX()方法時將有所區別,如setPartX()方法的參數可能不一樣,在有些具體建造者類中某些setPartX()方法無須實現(提供一個空實現)。而這些對於客戶端來說都無須關心,客戶端只需知道具體建造者類型即可。
在建造者模式的結構中還引入了一個指揮者類Director,該類主要有兩個作用:一方面它隔離了客戶與創建過程;另一方面它控制產品的創建過程,包括某個buildPartX()方法是否被調用以及多個buildPartX()方法調用的先後次序等。指揮者針對抽象建造者編程,客戶端只需要知道具體建造者的類型,即可通過指揮者類調用建造者的相關方法,返回一個完整的產品對象。在實際生活中也存在類似指揮者一樣的角色,如一個客戶去購買電腦,電腦銷售人員相當於指揮者,只要客戶確定電腦的類型,電腦銷售人員可以通知電腦組裝人員給客戶組裝一臺電腦。指揮者類的代碼示例如下:
class Director {
private Builder builder;
public Director(Builder builder) {
this.builder=builder;
}
public void setBuilder(Builder builder) {
this.builder=builer;
}
//產品構建與組裝方法
public Product construct() {
builder.buildPartA();
builder.buildPartB();
builder.buildPartC();
return builder.getResult();
}
}
在指揮者類中可以注入一個抽象建造者類型的對象,其核心在於提供了一個建造方法construct(),在該方法中調用了builder對象的構造部件的方法,最後返回一個產品對象。
對於客戶端而言,只需關心具體的建造者即可,一般情況下,客戶端類代碼片段如下所示:
Builder builder = new ConcreteBuilder(); //可通過配置文件實現
Director director = new Director(builder);
Product product = director.construct();
可以通過配置文件來存儲具體建造者類ConcreteBuilder的類名,使得更換新的建造者時無須修改源代碼,系統擴展更爲方便。在客戶端代碼中,無須關心產品對象的具體組裝過程,只需指定具體建造者的類型即可。
建造者模式與抽象工廠模式有點相似,但是建造者模式返回一個完整的複雜產品,而抽象工廠模式返回一系列相關的產品;在抽象工廠模式中,客戶端通過選擇具體工廠來生成所需對象,而在建造者模式中,客戶端通過指定具體建造者類型並指導Director類如何去生成對象,側重於一步步構造一個複雜對象,然後將結果返回。如果將抽象工廠模式看成一個汽車配件生產廠,生成不同類型的汽車配件,那麼建造者模式就是一個汽車組裝廠,通過對配件進行組裝返回一輛完整的汽車。
五、建造者模式總結
建造者模式的核心在於如何一步步構建一個包含多個組成部件的完整對象,使用相同的構建過程構建不同的產品,在軟件開發中,如果我們需要創建複雜對象並希望系統具備很好的靈活性和可擴展性可以考慮使用建造者模式。
主要優點
- 在建造者模式中,客戶端不必知道產品內部組成的細節,將產品本身與產品的創建過程解耦,使得相同的創建過程可以創建不同的產品對象。
- 每一個具體建造者都相對獨立,而與其他的具體建造者無關,因此可以很方便地替換具體建造者或增加新的具體建造者,用戶使用不同的具體建造者即可得到不同的產品對象。由於指揮者類針對抽象建造者編程,增加新的具體建造者無須修改原有類庫的代碼,系統擴展方便,符合“開閉原則”
- 可以更加精細地控制產品的創建過程。將複雜產品的創建步驟分解在不同的方法中,使得創建過程更加清晰,也更方便使用程序來控制創建過程。
主要缺點
- 建造者模式所創建的產品一般具有較多的共同點,其組成部分相似,如果產品之間的差異性很大,例如很多組成部分都不相同,不適合使用建造者模式,因此其使用範圍受到一定的限制。
- 如果產品的內部變化複雜,可能會導致需要定義很多具體建造者類來實現這種變化,導致系統變得很龐大,增加系統的理解難度和運行成本。
適用場景
- 需要生成的產品對象有複雜的內部結構,這些產品對象通常包含多個成員屬性。
- 需要生成的產品對象的屬性相互依賴,需要指定其生成順序。
- 對象的創建過程獨立於創建該對象的類。在建造者模式中通過引入了指揮者類,將創建過程封裝在指揮者類中,而不在建造者類和客戶類中。
- 隔離複雜對象的創建和使用,並使得相同的創建過程可以創建不同的產品。