一、前言
當一個類的內(nèi)部數(shù)據(jù)過于復雜(通常是負責持有數(shù)據(jù)的類,比如Config、VO、PO、Entity...),要創(chuàng)建這個類的實例時,了解內(nèi)部結構和裝配,學習成本太高了。為方便更好的管理這個類的數(shù)據(jù),以及代碼的可讀性好,Builder模式應運而生了,Builder模式可以將一個類的構建和表示進行分離。
二、什么是建造者模式
創(chuàng)建者模式又叫建造者模式,是將一個復雜的對象的構建與它的表示分離,使
得同樣的構建過程可以創(chuàng)建不同的表示。創(chuàng)建者模式隱藏了復雜對象的創(chuàng)建過程,它把復雜對象的創(chuàng)建過程加以抽象,通過子類繼承或者重載的方式,動態(tài)的創(chuàng)建具有復合屬性的對象。
三、為什么要使用建造者模式
當一個類的構造函數(shù)參數(shù)個數(shù)超過4個,而且這些參數(shù)有些是可選的參數(shù),考慮使用構造者模式。
3.1、建造者模式解決了什么問題
當一個類的構造函數(shù)參數(shù)超過4個,而且這些參數(shù)有些是可選的時,我們通常有兩種辦法來構建它的對象。 例如我們現(xiàn)在有如下一個類計算機類Computer,其中cpu與ram是必填參數(shù),而其他3個是可選參數(shù),那么我們?nèi)绾螛嬙爝@個類的實例呢,通常有兩種常用的方式:
public class Computer {
private String cpu;//cup 必須
private String ram;//內(nèi)存 必須
private int usbCount;//usb數(shù)量 可選
private String keyboard;//鍵盤 可選
private String display;//顯示器 可選
}
1、折疊構造函數(shù)模式(telescoping constructor pattern ),這個我們經(jīng)常用,如下代碼所示:
public class Computer {
...
public Computer(String cpu, String ram) {
this(cpu, ram, 0);
}
public Computer(String cpu, String ram, int usbCount) {
this(cpu, ram, usbCount, "雙飛燕鍵盤");
}
public Computer(String cpu, String ram, int usbCount, String keyboard) {
this(cpu, ram, usbCount, keyboard, "飛利浦顯示器");
}
public Computer(String cpu, String ram, int usbCount, String keyboard, String display) {
this.cpu = cpu;
this.ram = ram;
this.usbCount = usbCount;
this.keyboard = keyboard;
this.display = display;
}
}
2、Javabean 模式,如下所示:
public class Computer {
...
public String getCpu() {
return cpu;
}
public void setCpu(String cpu) {
this.cpu = cpu;
}
public String getRam() {
return ram;
}
public void setRam(String ram) {
this.ram = ram;
}
public int getUsbCount() {
return usbCount;
}
...
}
那么這兩種方式有什么弊端呢? 第一種主要是使用及閱讀不方便。當你要調(diào)用一個類的構造函數(shù)時,你首先要決定使用哪一個,然后里面又是一堆參數(shù),容易傳混傳錯。 第二種方式在構建過程中對象的狀態(tài)容易發(fā)生變化。因為類中的屬性是分步設置,屬性的值隨時在變化,容易造成錯誤。
四、建造者模式的結構

1、Product(產(chǎn)品角色):最終要生成的對象,例如 Computer實例。
2、Builder(抽象構造者):構建者的抽象基類(有時會使用接口代替)。其定義了構建Product的抽象步驟,其實體類需要實現(xiàn)這些步驟。其會包含一個用來返回最終產(chǎn)品的方法Product getProduct()。
3、ConcreteBuilder(具體構造者):Builder的實現(xiàn)類。
4、Director(指揮者):決定如何構建最終產(chǎn)品的算法. 其會包含一個負責組裝的方法void Construct(Builder builder), 在這個方法中通過調(diào)用builder的方法,就可以設置builder,等設置完成后,就可以通過builder的 getProduct() 方法獲得最終的產(chǎn)品。
五、簡化Builder方式代碼示例
5.1、目標產(chǎn)品和構造者
public class Computer {
private String cpu;//cup 必須
private String ram;//內(nèi)存 必須
private int usbCount;//usb數(shù)量 可選
private String keyboard;//鍵盤 可選
private String display;//顯示器 可選
public Computer(Builder builder) {
this.cpu=builder.cpu;
this.ram=builder.ram;
this.usbCount=builder.usbCount;
this.keyboard=builder.keyboard;
this.display=builder.display;
}
public static class Builder {
private String cpu;//cup 必須
private String ram;//內(nèi)存 必須
private int usbCount;//usb數(shù)量 可選
private String keyboard;//鍵盤 可選
private String display;//顯示器 可選
public Builder(String cup,String ram){
this.cpu=cup;
this.ram=ram;
}
public Builder setUsbCount(int usbCount) {
this.usbCount = usbCount;
return this;
}
public Builder setKeyboard(String keyboard) {
this.keyboard = keyboard;
return this;
}
public Builder setDisplay(String display) {
this.display = display;
return this;
}
public Computer build(){
return new Computer(this);
}
}
}
5.2、客戶端
public class Client {
public static void main(String[] args){
Computer computer=new Computer.Builder("AMD","金士頓")
.setDisplay("飛利浦24寸")
.setKeyboard("雙飛燕")
.setUsbCount(3)
.build();
}
}
六、傳統(tǒng)Builder模式代碼示例
6.1、目標產(chǎn)品類
public class Computer {
private String cpu;//cup 必須
private String ram;//內(nèi)存 必須
private int usbCount;//usb數(shù)量 可選
private String keyboard;//鍵盤 可選
private String display;//顯示器 可選
public Computer(String cpu, String ram) {
this.cpu = cpu;
this.ram = ram;
}
public void setUsbCount(int usbCount) {
this.usbCount = usbCount;
}
public void setKeyboard(String keyboard) {
this.keyboard = keyboard;
}
public void setDisplay(String display) {
this.display = display;
}
}
6.2、抽象構造者類
public abstract class Builder {
public abstract Builder usbCount (int usbCount);
public abstract Builder keyboard (String keyboard);
public abstract Builder display (String display);
public abstract Computer getComputer();
}
6.3、具體構造者類
public class ComputerBuilder extends Builder {
private Computer computer;
public ComputerBuilder(String cpu, String ram) {
this.computer = new Computer(cpu, ram);
}
@Override
public Builder usbCount(int usbCount) {
this.computer.setUsbCount(usbCount);
return this;
}
@Override
public Builder keyboard(String keyboard) {
this.computer.setKeyboard(keyboard);
return this;
}
@Override
public Builder display(String display) {
this.computer.setDisplay(display);
return this;
}
@Override
public Computer getComputer() {
return this.computer;
}
}
6.4、指揮者類
public class ComputerDirector {
public Computer construct(Builder builder){
return builder.getComputer();
}
}
6.5、客戶端
public class Client {
public static void main(String[] args){
Builder builder = new ComputerBuilder("AMD", "金士頓")
.usbCount(3)
.keyboard("雙飛燕")
.display("飛利浦24寸");
ComputerDirector director = new ComputerDirector();
Computer computer = director.construct(builder);
}
}
七、建造者模式的優(yōu)缺點
7.1、優(yōu)點
1、客戶端不必知道產(chǎn)品內(nèi)部的組成細節(jié),將產(chǎn)品本身與產(chǎn)品的建造過程解耦,使得相同的建造過程可以建造不同的產(chǎn)品對象。
2、每個具體建筑者對象都是獨立的,與其他的具體建造者無關,因此很方便地替換具體建筑者或者增加新的具體建造者。
3、可以更加精細地控制產(chǎn)品的建造過程。將復雜產(chǎn)品的創(chuàng)建步驟分解在不同的方法中,使得創(chuàng)建過程更加清晰,也更加方便使用程序來控制創(chuàng)建過程。
7.2、缺點
1、建造者模式所創(chuàng)建的產(chǎn)品一般具有較多的共同點,其組成部分相似;如果產(chǎn)品之間的差異性很大,則不適合使用建造者模式,因此其使用范圍受到一定的限制。
2、如果產(chǎn)品的內(nèi)部變化復雜,可能會導致需要定義很多具體建造者類來實現(xiàn)這種變化,導致系統(tǒng)變得很龐大。
八、建造者和抽象工廠模式的區(qū)別
建造者模式完成的事,好像都可以通過抽象工廠模式完成,那么區(qū)別是什么?
1、與建造者模式相比,抽象工廠模式返回的是一系列相關的產(chǎn)品,這些產(chǎn)品位于不同的產(chǎn)品等級結構,構成產(chǎn)品族,而建造者模式返回的是一個組裝好的完整產(chǎn)品。抽象工廠模式更像一個汽車零件生產(chǎn)商,生產(chǎn)不同品牌汽車的各種零件。而建造者模式更像一個汽車裝配廠,通過一系列零件的組裝,最終生產(chǎn)出的是一個完整的汽車。
2、抽象工廠模式中,客戶端需實例化工廠類,然后通過工廠類獲取所需的產(chǎn)品對象。而建造者模式更側重于將復雜的構造對象的方法交給建造者去做,而客戶端只需要通過指揮者就能創(chuàng)建一個完整的產(chǎn)品實例。