背景
當(dāng)一個(gè)類(lèi)的內(nèi)部數(shù)據(jù)過(guò)于復(fù)雜的時(shí)候(通常是負(fù)責(zé)持有數(shù)據(jù)的類(lèi)霸奕,比如Config溜宽、VO、PO质帅、Entity...)适揉,要?jiǎng)?chuàng)建的話(huà)可能就需要了解這個(gè)類(lèi)的內(nèi)部結(jié)構(gòu),還有這些東西是怎么組織裝配等一大坨亂七八糟的東西煤惩,這個(gè)時(shí)候就會(huì)增加學(xué)習(xí)成本而且會(huì)很混亂嫉嘀,這個(gè)時(shí)候就想啊想一種什么法子來(lái)管理一下這個(gè)類(lèi)中的數(shù)據(jù)呢,怎么在創(chuàng)建的時(shí)候讓它按部就班的來(lái)魄揉,并且代碼可讀性很好別讓我看花了眼啊剪侮,我要的東西也能都很好設(shè)置進(jìn)來(lái),這就是Builder模式的應(yīng)用場(chǎng)景什猖,Builder模式可以將一個(gè)類(lèi)的構(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ò)子類(lèi)繼承或者重載的方式推掸,動(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)品類(lèi)非常復(fù)雜或者產(chǎn)品類(lèi)因?yàn)檎{(diào)用順序不同而產(chǎn)生不同作用
- 初始化一個(gè)對(duì)象時(shí)毡泻,參數(shù)過(guò)多,或者很多參數(shù)具有默認(rèn)值
- Builder模式不適合創(chuàng)建差異性很大的產(chǎn)品類(lèi)
產(chǎn)品內(nèi)部變化復(fù)雜粘优,會(huì)導(dǎo)致需要定義很多具體建造者類(lèi)實(shí)現(xiàn)變化仇味,增加項(xiàng)目中類(lèi)的數(shù)量,增加系統(tǒng)的理解難度和運(yùn)行成本 - 需要生成的產(chǎn)品對(duì)象有復(fù)雜的內(nèi)部結(jié)構(gòu)雹顺,這些產(chǎn)品對(duì)象具備共性丹墨;
1.3 主要作用
在用戶(hù)不知道對(duì)象的建造過(guò)程和細(xì)節(jié)的情況下就可以直接創(chuàng)建復(fù)雜的對(duì)象。
- 用戶(hù)只需要給出指定復(fù)雜對(duì)象的類(lèi)型和內(nèi)容嬉愧;
- 建造者模式負(fù)責(zé)按順序創(chuàng)建復(fù)雜對(duì)象(把內(nèi)部的建造過(guò)程和細(xì)節(jié)隱藏起來(lái))
1.4 解決的問(wèn)題
- 方便用戶(hù)創(chuàng)建復(fù)雜的對(duì)象(不需要知道實(shí)現(xiàn)過(guò)程)
- 代碼復(fù)用性 & 封裝性(將對(duì)象構(gòu)建過(guò)程和細(xì)節(jié)進(jìn)行封裝 & 復(fù)用)
例子:造汽車(chē) & 買(mǎi)汽車(chē)贩挣。
- 工廠(建造者模式):負(fù)責(zé)制造汽車(chē)(組裝過(guò)>程和細(xì)節(jié)在工廠內(nèi))
- 汽車(chē)購(gòu)買(mǎi)者(用戶(hù)):你只需要說(shuō)出你需要的>型號(hào)(對(duì)象的類(lèi)型和內(nèi)容),然后直接購(gòu)買(mǎi)就可>>以使用了
(不需要知道汽車(chē)是怎么組裝的(車(chē)輪、車(chē)門(mén)王财、>發(fā)動(dòng)機(jī)卵迂、方向盤(pán)等等))
2. 模式原理
2.1 UML類(lèi)圖 & 組成
2.2模式講解:
- 指揮者(Director)直接和客戶(hù)(Client)進(jìn)行需求溝通;
- 溝通后指揮者將客戶(hù)創(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ē)為例子论颅,示例代碼:
產(chǎn)品類(lèi):
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 類(lèi):
// 抽象 builder 類(lèi)
public abstract class Builder {
abstract void buildFrame();
abstract void buildSeat();
abstract void buildTire();
abstract Bike createBike();
}
ConcreteBuilder 類(lèi) :
// 具體 builder 類(lèi)
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;
}
}
指揮者類(lèi):
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();
}
}
客戶(hù)端使用:
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)演類(lèi) Director 在 Builder模式中具有很重要的作用囱嫩,它用于指導(dǎo)具體構(gòu)建者如何構(gòu)建產(chǎn)品恃疯,控制調(diào)用先后次序,并向調(diào)用者返回完整的產(chǎn)品類(lèi)墨闲,但是有些情況下需要簡(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)演類(lèi)中的construct()方法合并到抽象建造者類(lèi)中
*
* @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í)也加重了抽象建造者類(lèi)的職責(zé)盾鳞,也不是太符合單一職責(zé)原則,如果construct() 過(guò)于復(fù)雜瞻离,建議還是封裝到 Director 中
除了上面的用途外腾仅,還有另外一個(gè)常用的使用方式,就是當(dāng)一個(gè)類(lèi)構(gòu)造器需要傳入很多參數(shù)時(shí)套利,如果創(chuàng)建這個(gè)類(lèi)的實(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);}
}
}
客戶(hù)端:
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ù)真的很多的話(huà)鬼知道每個(gè)對(duì)應(yīng)的是什么意思啊。
Builder模式:優(yōu)點(diǎn)是可以將構(gòu)造器的setter方法名取成類(lèi)似注釋的方式联四,這樣我們可以很清晰的知道剛才究竟設(shè)置的什么值,可讀性較高撑教,缺點(diǎn)是比較冗長(zhǎng)朝墩。
優(yōu)點(diǎn):
- 使用建造者模式可以使客戶(hù)端不必知道產(chǎn)品內(nèi)部組成的細(xì)節(jié)。
- 具體的建造者類(lèi)之間是相互獨(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)來(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)品族 伦乔。
- 在抽象工廠模式中,客戶(hù)端實(shí)例化工廠類(lèi)董习,然后調(diào)用工廠方法獲取所需產(chǎn)品對(duì)象烈和,而在建造者模式中,客戶(hù)端可以不直接調(diào)用建造者的相關(guān)方法阱飘,而是通過(guò)指揮者類(lèi)來(lái)指導(dǎo)如何生成對(duì)象斥杜,包括對(duì)象的組裝過(guò)程和建造步驟,它側(cè)重于一步步構(gòu)造一個(gè)復(fù)雜對(duì)象沥匈,返回一個(gè)完整的對(duì)象 蔗喂。
- 如果將抽象工廠模式看成汽車(chē)配件生產(chǎn)工廠,生產(chǎn)一個(gè)產(chǎn)品族的產(chǎn)品高帖,那么建造者模式就是一個(gè)汽車(chē)組裝工廠缰儿,通過(guò)對(duì)部件的組裝可以返回一輛完整的汽車(chē)
改文章已同步到公眾號(hào),歡迎大家關(guān)注散址!