背景
當(dāng)一個(gè)類的內(nèi)部數(shù)據(jù)過(guò)于復(fù)雜的時(shí)候(通常是負(fù)責(zé)持有數(shù)據(jù)的類,比如Config、VO、PO、Entity...),要?jiǎng)?chuàng)建的話可能就需要了解這個(gè)類的內(nèi)部結(jié)構(gòu),還有這些東西是怎么組織裝配等一大坨亂七八糟的東西,這個(gè)時(shí)候就會(huì)增加學(xué)習(xí)成本而且會(huì)很混亂,這個(gè)時(shí)候就想啊想一種什么法子來(lái)管理一下這個(gè)類中的數(shù)據(jù)呢,怎么在創(chuàng)建的時(shí)候讓它按部就班的來(lái),并且代碼可讀性很好別讓我看花了眼啊,我要的東西也能都很好設(shè)置進(jìn)來(lái),這就是Builder模式的應(yīng)用場(chǎng)景,Builder模式可以將一個(gè)類的構(gòu)建和表示進(jìn)行分離。
1.介紹
1.1什么是構(gòu)建者模式
創(chuàng)建者模式又叫建造者模式,是將一個(gè)復(fù)雜的對(duì)象的構(gòu)建與它的表示分離,使
得同樣的構(gòu)建過(guò)程可以創(chuàng)建不同的表示。創(chuàng)建者模式隱藏了復(fù)雜對(duì)象的創(chuàng)建過(guò)程,它把復(fù)雜對(duì)象的創(chuàng)建過(guò)程加以抽象,通過(guò)子類繼承或者重載的方式,動(dòng)態(tài)的創(chuàng)建具有復(fù)合屬性的對(duì)象。
1.2適用場(chǎng)景:
- 隔離復(fù)雜對(duì)象的創(chuàng)建和使用,相同的方法,不同執(zhí)行順序,產(chǎn)生不同事件結(jié)果
- 多個(gè)部件都可以裝配到一個(gè)對(duì)象中,但產(chǎn)生的運(yùn)行結(jié)果不相同
- 產(chǎn)品類非常復(fù)雜或者產(chǎn)品類因?yàn)檎{(diào)用順序不同而產(chǎn)生不同作用
- 初始化一個(gè)對(duì)象時(shí),參數(shù)過(guò)多,或者很多參數(shù)具有默認(rèn)值
- Builder模式不適合創(chuàng)建差異性很大的產(chǎn)品類
產(chǎn)品內(nèi)部變化復(fù)雜,會(huì)導(dǎo)致需要定義很多具體建造者類實(shí)現(xiàn)變化,增加項(xiàng)目中類的數(shù)量,增加系統(tǒng)的理解難度和運(yùn)行成本 - 需要生成的產(chǎn)品對(duì)象有復(fù)雜的內(nèi)部結(jié)構(gòu),這些產(chǎn)品對(duì)象具備共性;
1.3 主要作用
在用戶不知道對(duì)象的建造過(guò)程和細(xì)節(jié)的情況下就可以直接創(chuàng)建復(fù)雜的對(duì)象。
- 用戶只需要給出指定復(fù)雜對(duì)象的類型和內(nèi)容;
- 建造者模式負(fù)責(zé)按順序創(chuàng)建復(fù)雜對(duì)象(把內(nèi)部的建造過(guò)程和細(xì)節(jié)隱藏起來(lái))
1.4 解決的問(wèn)題
- 方便用戶創(chuàng)建復(fù)雜的對(duì)象(不需要知道實(shí)現(xiàn)過(guò)程)
- 代碼復(fù)用性 & 封裝性(將對(duì)象構(gòu)建過(guò)程和細(xì)節(jié)進(jìn)行封裝 & 復(fù)用)
例子:造汽車 & 買汽車。
- 工廠(建造者模式):負(fù)責(zé)制造汽車(組裝過(guò)>程和細(xì)節(jié)在工廠內(nèi))
- 汽車購(gòu)買者(用戶):你只需要說(shuō)出你需要的>型號(hào)(對(duì)象的類型和內(nèi)容),然后直接購(gòu)買就可>>以使用了
(不需要知道汽車是怎么組裝的(車輪、車門(mén)、>發(fā)動(dòng)機(jī)、方向盤(pán)等等))
2. 模式原理
2.1 UML類圖 & 組成

2.2模式講解:
- 指揮者(Director)直接和客戶(Client)進(jìn)行需求溝通;
- 溝通后指揮者將客戶創(chuàng)建產(chǎn)品的需求劃分為各個(gè)部件的建造請(qǐng)求(Builder);
- 將各個(gè)部件的建造請(qǐng)求委派到具體的建造者(ConcreteBuilder);
- 各個(gè)具體建造者負(fù)責(zé)進(jìn)行產(chǎn)品部件的構(gòu)建;
- 最終構(gòu)建成具體產(chǎn)品(Product)。
3.用 builder 模式創(chuàng)建共享單車為例子,示例代碼:
產(chǎn)品類:
public class Bike {
private IFrame frame;
private ISeat seat;
private ITire tire;
public IFrame getFrame() {
return frame;
}
public void setFrame(IFrame frame) {
this.frame = frame;
}
public ISeat getSeat() {
return seat;
}
public void setSeat(ISeat seat) {
this.seat = seat;
}
public ITire getTire() {
return tire;
}
public void setTire(ITire tire) {
this.tire = tire;
}
}
Builder 類:
// 抽象 builder 類
public abstract class Builder {
abstract void buildFrame();
abstract void buildSeat();
abstract void buildTire();
abstract Bike createBike();
}
ConcreteBuilder 類 :
// 具體 builder 類
public class MobikeBuilder extends Builder{
private Bike mBike = new Bike();
@Override
void buildFrame() {
mBike.setFrame(new AlloyFrame());
}
@Override
void buildSeat() {
mBike.setSeat(new DermisSeat());
}
@Override
void buildTire() {
mBike.setTire(new SolidTire());
}
@Override
Bike createBike() {
return mBike;
}
}
public class OfoBuilder extends Builder{
private Bike mBike = new Bike();
@Override
void buildFrame() {
mBike.setFrame(new CarbonFrame());
}
@Override
void buildSeat() {
mBike.setSeat(new RubberSeat());
}
@Override
void buildTire() {
mBike.setTire(new InflateTire());
}
@Override
Bike createBike() {
return mBike;
}
}
指揮者類:
public class Director {
private Builder mBuilder = null;
public Director(Builder builder) {
mBuilder = builder;
}
public Bike construct() {
mBuilder.buildFrame();
mBuilder.buildSeat();
mBuilder.buildTire();
return mBuilder.createBike();
}
}
客戶端使用:
public class Click {
public static void main(String[] args) {
showBike(new OfoBuilder());
showBike(new MobikeBuilder());
}
private void showBike(Builder builder) {
Director director = new Director(builder);
Bike bike = director.construct();
bike.getFrame().frame();
bike.getSeat().seat();
bike.getTire().tire();
}
}
上面示例是 Builder模式的常規(guī)用法,導(dǎo)演類 Director 在 Builder模式中具有很重要的作用,它用于指導(dǎo)具體構(gòu)建者如何構(gòu)建產(chǎn)品,控制調(diào)用先后次序,并向調(diào)用者返回完整的產(chǎn)品類,但是有些情況下需要簡(jiǎn)化系統(tǒng)結(jié)構(gòu),可以把Director和抽象建造者進(jìn)行結(jié)合,示例代碼:
改造后的抽象建造者:
public abstract class NewBuilder {
abstract void buildFrame();
abstract void buildSeat();
abstract void buildTire();
abstract Bike createBike();
/**
* 把導(dǎo)演類中的construct()方法合并到抽象建造者類中
*
* @return 具體產(chǎn)品對(duì)象
*/
public Bike construct() {
this.buildFrame();
this.buildSeat();
this.buildTire();
return this.createBike();
}
}
這樣做確實(shí)簡(jiǎn)化了系統(tǒng)結(jié)構(gòu),但同時(shí)也加重了抽象建造者類的職責(zé),也不是太符合單一職責(zé)原則,如果construct() 過(guò)于復(fù)雜,建議還是封裝到 Director 中
除了上面的用途外,還有另外一個(gè)常用的使用方式,就是當(dāng)一個(gè)類構(gòu)造器需要傳入很多參數(shù)時(shí),如果創(chuàng)建這個(gè)類的實(shí)例,代碼可讀性會(huì)非常差,而且很容易引入錯(cuò)誤,此時(shí)就可以利用 builder模式進(jìn)行重構(gòu),重構(gòu)前示例代碼:
// 省略 getter 和 setter 方法
public class Computer {
private String cpu;
private String screen;
private String memory;
private String mainboard;
public Computer(String cpu, String screen, String memory, String mainboard) {
this.cpu = cpu;
this.screen = screen;
this.memory = memory;
this.mainboard = mainboard;
}
}
public class NewComputer {
private String cpu;
private String screen;
private String memory;
private String mainboard;
public NewComputer() {
throw new RuntimeException(“can’t init”);
}
private NewComputer(Builder builder) {
cpu = builder.cpu;
screen = builder.screen;
memory = builder.memory;
mainboard = builder.mainboard;
}
public static final class Builder {
private String cpu;
private String screen;
private String memory;
private String mainboard;
public Builder() {}
public Builder cpu(String val) {
cpu = val;
return this;
}
public Builder screen(String val) {
screen = val;
return this;
}
public Builder memory(String val) {
memory = val;
return this;
}
public Builder mainboard(String val) {
mainboard = val;
return this;
}
public NewComputer build() {
return new NewComputer(this);}
}
}
客戶端:
public class Click {
public static void main(String[] args) {
// 非 Builder 模式
Computer computer = new Computer(“cpu”, “screen”, “memory”, “mainboard”);
// Builder 模式
NewComputer newComputer = new NewComputer.Builder()
.cpu(“cpu”)
.screen(“screen”)
.memory(“memory”)
.mainboard(“mainboard”)
.build();
}
}
上面的示例代碼只是傳入四個(gè)參數(shù),如果參數(shù)是十四個(gè)甚至更多,builder 模式的優(yōu)勢(shì)將會(huì)更加明顯,傳遞參數(shù)更加靈活,代碼具有更高的可讀性.
4.優(yōu)缺點(diǎn)比較
一般的套路:優(yōu)點(diǎn)是比較簡(jiǎn)單,開(kāi)發(fā)效率高,缺點(diǎn)是如果參數(shù)真的很多的話鬼知道每個(gè)對(duì)應(yīng)的是什么意思啊。
Builder模式:優(yōu)點(diǎn)是可以將構(gòu)造器的setter方法名取成類似注釋的方式,這樣我們可以很清晰的知道剛才究竟設(shè)置的什么值,可讀性較高,缺點(diǎn)是比較冗長(zhǎng)。
優(yōu)點(diǎn):
- 使用建造者模式可以使客戶端不必知道產(chǎn)品內(nèi)部組成的細(xì)節(jié)。
- 具體的建造者類之間是相互獨(dú)立的,這有利于系統(tǒng)的擴(kuò)展。
- 具體的建造者相互獨(dú)立,因此可以對(duì)建造的過(guò)程逐步細(xì)化,而不會(huì)對(duì)其他模塊產(chǎn)生任何影響。
缺點(diǎn):
- 建造者模式所創(chuàng)建的產(chǎn)品一般具有較多的共同點(diǎn),其組成部分相似;如果產(chǎn)品之間的差異性很大,則不適合使用建造者模式,因此其使用范圍受到一定的限制。
- 如果產(chǎn)品的內(nèi)部變化復(fù)雜,可能會(huì)導(dǎo)致需要定義很多具體建造者類來(lái)實(shí)現(xiàn)這種變化,導(dǎo)致系統(tǒng)變得很龐大。
5.建造者模式與抽象工廠模式的比較:
- 與抽象工廠模式相比,建造者模式返回一個(gè)組裝好的完整產(chǎn)品,而抽象工廠模式返回一系列相關(guān)的產(chǎn)品,這些產(chǎn)品位于不同的產(chǎn)品等級(jí)結(jié)構(gòu),構(gòu)成了一個(gè)產(chǎn)品族 。
- 在抽象工廠模式中,客戶端實(shí)例化工廠類,然后調(diào)用工廠方法獲取所需產(chǎn)品對(duì)象,而在建造者模式中,客戶端可以不直接調(diào)用建造者的相關(guān)方法,而是通過(guò)指揮者類來(lái)指導(dǎo)如何生成對(duì)象,包括對(duì)象的組裝過(guò)程和建造步驟,它側(cè)重于一步步構(gòu)造一個(gè)復(fù)雜對(duì)象,返回一個(gè)完整的對(duì)象 。
- 如果將抽象工廠模式看成汽車配件生產(chǎn)工廠,生產(chǎn)一個(gè)產(chǎn)品族的產(chǎn)品,那么建造者模式就是一個(gè)汽車組裝工廠,通過(guò)對(duì)部件的組裝可以返回一輛完整的汽車
改文章已同步到公眾號(hào),歡迎大家關(guān)注!
