設計模式之建造者模式(BuilderPattern)
點擊上方藍色字體,選擇“標星公眾號”
優(yōu)質文章,第一時間送達
作者 | 敲代碼的小小酥
來源 | urlify.cn/zmuaey
一.意義
將一個復雜的對象的構建與它的表示分離,使得同樣的構建過程可以創(chuàng)建不同的表示。
說明:復雜對象的構建,比如一個對象有幾十個成員屬性,那么我們在創(chuàng)建這個對象,并給成員屬性賦值時,就會很麻煩。采用建造者模式,就是把創(chuàng)建對象并給成員屬性賦值的工作,分離出來,由建造者角色來完成,業(yè)務程序員直接調(diào)用導演類,獲得復雜對象即可,無需再進行對象的創(chuàng)建工作了。
二.角色
建造者模式涉及到一下幾種角色:
Builder(抽象建造者):它為創(chuàng)建一個產(chǎn)品Product對象的各個部件指定抽象接口,在該接口中一般聲明兩類方法,一類方法是buildPartX(),它們用于創(chuàng)建復雜對象的各個部件;另一類方法是getResult(),它們用于返回復雜對象。Builder既可以是抽象類,也可以是接口。由此可見,抽象建造者就類似于抽象工廠,只不過,抽象工廠是調(diào)度的產(chǎn)品的整體部件,而抽象建造者,是構造產(chǎn)品的內(nèi)部部件。抽象建造者定義了建造規(guī)范,不同的建造者遵循著同樣的建造規(guī)范,構建不同的部件。
ConcreteBuilder(具體建造者):它實現(xiàn)了Builder接口,實現(xiàn)各個部件的具體構造和裝配方法,定義并明確它所創(chuàng)建的復雜對象,也可以提供一個方法返回創(chuàng)建好的復雜產(chǎn)品對象。具體建造者就相當于工廠實現(xiàn)類。一個產(chǎn)品,對應一個具體建造者,構造多樣的部件,并返回對應的產(chǎn)品對象。
Product(產(chǎn)品角色):它是被構建的復雜對象,包含多個組成部件,具體建造者創(chuàng)建該產(chǎn)品的內(nèi)部表示并定義它的裝配過程。在建造者模式中,產(chǎn)品大多是一個類,產(chǎn)品的部件就是類的成員屬性。
Director(指揮者):指揮者又稱為導演類,它負責安排復雜對象的建造次序,指揮者與抽象建造者之間存在關聯(lián)關系,可以在其construct()建造方法中調(diào)用建造者對象的部件構造與裝配方法,完成復雜對象的建造。客戶端一般只需要與指揮者進行交互,在客戶端確定具體建造者的類型,并實例化具體建造者對象(也可以通過配置文件和反射機制),然后通過指揮者類的構造函數(shù)或者Setter方法將該對象傳入指揮者類中。
個人感覺,這里的導演類,完全可以融合到建造者角色中,這樣的話,對于調(diào)用者而言,選擇具體建造者的時候,通過建造者構造部件,也可以獲得產(chǎn)品。這種形式,就是把產(chǎn)品的構建和產(chǎn)品的獲得,都放到了一個類里,違反了單一責任原則。所以,導演類的存在,是為了滿足單一責任原則。那為什么在工廠模式中,沒有導演這個角色呢,那是因為在工廠模式中,工廠的作用就是生產(chǎn)產(chǎn)品,直接通過工廠,就獲得了產(chǎn)品。而建造者模式,建造者是在構建產(chǎn)品,它體現(xiàn)的是一個過程,而不是最終的結果。
三、代碼實現(xiàn)
我們來構建上述的角色,實現(xiàn)建造者模式:
首先,先定義產(chǎn)品,我們以電腦產(chǎn)品為例,建造者模式的產(chǎn)品以類為主,注重的是內(nèi)部的部件(成員屬性):
/**
* 產(chǎn)品:電腦
*/
public class Computer {
/**
* 內(nèi)部部件:
*/
private String brand;
private String cpu;
private String mainBoard;
private String hardDisk;
private String displayCard;
private String power;
private String memory;
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
public String getCpu() {
return cpu;
}
public void setCpu(String cpu) {
this.cpu = cpu;
}
public String getMainBoard() {
return mainBoard;
}
public void setMainBoard(String mainBoard) {
this.mainBoard = mainBoard;
}
public String getHardDisk() {
return hardDisk;
}
public void setHardDisk(String hardDisk) {
this.hardDisk = hardDisk;
}
public String getDisplayCard() {
return displayCard;
}
public void setDisplayCard(String displayCard) {
this.displayCard = displayCard;
}
public String getPower() {
return power;
}
public void setPower(String power) {
this.power = power;
}
public String getMemory() {
return memory;
}
public void setMemory(String memory) {
this.memory = memory;
}
} 然后,我們定義抽象創(chuàng)建者,主要分為兩部分:buildX構建部件,和getResult獲取構建產(chǎn)品,代碼如下:
**
* 建造者角色,其實就是代替復雜對象的構造函數(shù)等構造方式,在這里對復雜對象進行構建
*/
public abstract class ComputerBuilder {
//建造者中,創(chuàng)建復雜產(chǎn)品對象,構建出復雜對象后,進行輸出。
protected Computer computer = new Computer();
/**
* 建造產(chǎn)品部件的方法,這里用抽象方法,不同的建造者可以構建出不同的產(chǎn)品部件來。
*/
public abstract void buildBrand();
public abstract void buildCPU();
public abstract void buildMainBoard();
public abstract void buildHardDisk();
public abstract void buildDisplayCard();
public abstract void buildPower();
public abstract void buildMemory();
/**
* 建造者最后輸出復雜產(chǎn)品。
* @return
*/
public Computer createComputer() {
return computer;
}
}然后創(chuàng)建具體建造者類,如下代碼:
/**
* 具體建造者,可以構建不同的產(chǎn)品部件。構建者有統(tǒng)一的構建接口,這樣可以規(guī)范不同構建者。
*/
public class ASUSComputerBuilder extends ComputerBuilder{
@Override
public void buildBrand() {
computer.setBrand("華碩電腦");
}
@Override
public void buildCPU() {
computer.setCpu("Intel 第8代 酷睿");
}
@Override
public void buildMainBoard() {
computer.setMainBoard("華碩主板");
}
@Override
public void buildHardDisk() {
computer.setHardDisk("256GB SSD");
}
@Override
public void buildDisplayCard() {
computer.setDisplayCard("MX150 獨立2GB");
}
@Override
public void buildPower() {
computer.setPower("3芯 鋰離子電池 65W AC適配器");
}
@Override
public void buildMemory() {
computer.setMemory("1 x SO-DIMM 8GB");
}
}
/**
* 具體建造者,可以構建不同的產(chǎn)品部件。構建者有統(tǒng)一的構建接口,這樣可以規(guī)范不同構建者。
*/
public class DellComputerBuilder extends ComputerBuilder {
@Override
public void buildBrand() {
computer.setBrand("戴爾電腦");
}
@Override
public void buildCPU() {
computer.setCpu("i5-8300H 四核");
}
@Override
public void buildMainBoard() {
computer.setMainBoard("戴爾主板");
}
@Override
public void buildHardDisk() {
computer.setHardDisk("1T + 128GB SSD");
}
@Override
public void buildDisplayCard() {
computer.setDisplayCard("GTX1060 獨立6GB");
}
@Override
public void buildPower() {
computer.setPower("4芯 鋰離子電池 180W AC適配器");
}
@Override
public void buildMemory() {
computer.setMemory("4G + 4G");
}
}然后,我們定義導演類,來調(diào)度建造者,輸出復雜產(chǎn)品:
/**
* 導演類,指揮構建過程,調(diào)用建造者類。
*/
public class ComputerDirector {
/**
* 導演類的建造方法一般都用construct命名。傳入具體建造者類,輸出產(chǎn)品
* @param builder
* @return
*/
public Computer construct(ComputerBuilder builder){
Computer computer;
builder.buildBrand();
builder.buildCPU();
builder.buildDisplayCard();
builder.buildHardDisk();
builder.buildMainBoard();
builder.buildMemory();
builder.buildPower();
computer = builder.createComputer();
return computer;
}
}這樣,對于業(yè)務程序員而言,就可以通過導演類,來獲取復雜對象了,如下:
public class Main {
public static void main(String[] args) {
//用戶直接使用的是導演類
ComputerDirector director=new ComputerDirector();
//選擇相應的構建者,來構建不同的部件
ComputerBuilder dellBuilder = new DellComputerBuilder();
//返回構建出來的產(chǎn)品
Computer dellComputer=director.construct(dellBuilder);
}
}由此我們可以看到,業(yè)務程序員需要選擇具體的建造類,傳入導演類中,獲得產(chǎn)品。在我們實際項目中,通常我們在xml里定義需要的具體建造者類,然后spring等其他框架讀取我們在xml里寫的具體建造者類,調(diào)用導演類,來獲得程序員想要的產(chǎn)品。所以對于業(yè)務程序員而言,需要做的就是在xml里定義具體建造者,導演類都是框架在做。
四、優(yōu)點和缺點
優(yōu)點:
在建造者模式中,客戶端不必知道產(chǎn)品內(nèi)部組成的細節(jié),將產(chǎn)品本身與產(chǎn)品的創(chuàng)建過程解耦,使得相同的創(chuàng)建過程可以創(chuàng)建不同的產(chǎn)品對象。
每一個具體建造者都相對獨立,而與其他的具體建造者無關,因此可以很方便地替換具體建造者或增加新的具體建造者,用戶使用不同的具體建造者即可得到不同的產(chǎn)品對象。由于指揮者類針對抽象建造者編程,增加新的具體建造者無須修改原有類庫的代碼,系統(tǒng)擴展方便,符合 “開閉原則”。
可以更加精細地控制產(chǎn)品的創(chuàng)建過程。將復雜產(chǎn)品的創(chuàng)建步驟分解在不同的方法中,使得創(chuàng)建過程更加清晰,也更方便使用程序來控制創(chuàng)建過程。
缺點:
建造者模式所創(chuàng)建的產(chǎn)品一般具有較多的共同點,其組成部分相似,如果產(chǎn)品之間的差異性很大,例如很多組成部分都不相同,不適合使用建造者模式,因此其使用范圍受到一定的限制。
如果產(chǎn)品的內(nèi)部變化復雜,可能會導致需要定義很多具體建造者類來實現(xiàn)這種變化,導致系統(tǒng)變得很龐大,增加系統(tǒng)的理解難度和運行成本。從這句話我們可以看出,在我們應用設計模式時,也要考慮系統(tǒng)的復雜程度,即使一個業(yè)務很適合應用某種設計模式,但是應用起來,很笨重很復雜,那么我們也不建議生搬硬套設計模式。
五、建造者模式的本質
建造者模式的本質,就是建造對象。我們在閱讀源碼中,看到Builder字樣時,只要知道其實在創(chuàng)造產(chǎn)品即可。具體其怎么走流程,怎么創(chuàng)建這個對象的,我們不用太過關注,因為有些建造者模式,結構和設計特別復雜。
粉絲福利:Java從入門到入土學習路線圖
??????

??長按上方微信二維碼 2 秒
感謝點贊支持下哈 
