大豐網(wǎng)站建設(shè)全網(wǎng)推廣軟件
1、概述
將一個(gè)復(fù)雜對(duì)象的構(gòu)建與表示分離,使得同樣的構(gòu)建過(guò)程可以創(chuàng)建不同的表示。
-
分離了部件的構(gòu)造(由Builder來(lái)負(fù)責(zé))和裝配(由Director負(fù)責(zé))。 從而可以構(gòu)造出復(fù)雜的對(duì)象。這個(gè)模式適用于:某個(gè)對(duì)象的構(gòu)建過(guò)程復(fù)雜的情況。
-
由于實(shí)現(xiàn)了構(gòu)建和裝配的解耦。不同的構(gòu)建器,相同的裝配,也可以做出不同的對(duì)象;相同的構(gòu)建器,不同的裝配順序也可以做出不同的對(duì)象。也就是實(shí)現(xiàn)了構(gòu)建算法、裝配算法的解耦,實(shí)現(xiàn)了更好的復(fù)用。
-
建造者模式可以將部件和其組裝過(guò)程分開,一步一步創(chuàng)建一個(gè)復(fù)雜的對(duì)象。用戶只需要指定復(fù)雜對(duì)象的類型就可以得到該對(duì)象,而無(wú)須知道其內(nèi)部的具體構(gòu)造細(xì)節(jié)。
2、結(jié)構(gòu)
建造者(Builder)模式包含如下角色:
-
抽象建造者類(Builder):這個(gè)接口規(guī)定要實(shí)現(xiàn)復(fù)雜對(duì)象的那些部分的創(chuàng)建,并不涉及具體的部件對(duì)象的創(chuàng)建。
-
具體建造者類(ConcreteBuilder):實(shí)現(xiàn) Builder 接口,完成復(fù)雜產(chǎn)品的各個(gè)部件的具體創(chuàng)建方法。在構(gòu)造過(guò)程完成后,提供產(chǎn)品的實(shí)例。
-
產(chǎn)品類(Product):要?jiǎng)?chuàng)建的復(fù)雜對(duì)象。
-
指揮者類(Director):調(diào)用具體建造者來(lái)創(chuàng)建復(fù)雜對(duì)象的各個(gè)部分,在指導(dǎo)者中不涉及具體產(chǎn)品的信息,只負(fù)責(zé)保證對(duì)象各部分完整創(chuàng)建或按某種順序創(chuàng)建。
類圖如下:
3、實(shí)例
創(chuàng)建共享單車
生產(chǎn)自行車是一個(gè)復(fù)雜的過(guò)程,它包含了車架,車座等組件的生產(chǎn)。而車架又有碳纖維,鋁合金等材質(zhì)的,車座有橡膠,真皮等材質(zhì)。對(duì)于自行車的生產(chǎn)就可以使用建造者模式。
這里Bike是產(chǎn)品,包含車架,車座等組件;Builder是抽象建造者,MobikeBuilder和OfoBuilder是具體的建造者;Director是指揮者。類圖如下:
具體的代碼如下:
//自行車類
public class Bike {private String frame;private String seat;
?public String getFrame() {return frame;}
?public void setFrame(String frame) {this.frame = frame;}
?public String getSeat() {return seat;}
?public void setSeat(String seat) {this.seat = seat;}
}
?
// 抽象 builder 類
public abstract class Builder {
?protected Bike mBike = new Bike();
?public abstract void buildFrame();public abstract void buildSeat();public abstract Bike createBike();
}
?
//摩拜單車Builder類
public class MobikeBuilder extends Builder {
?@Overridepublic void buildFrame() {mBike.setFrame("鋁合金車架");}
?@Overridepublic void buildSeat() {mBike.setSeat("真皮車座");}
?@Overridepublic Bike createBike() {return mBike;}
}
?
//ofo單車Builder類
public class OfoBuilder extends Builder {
?@Overridepublic void buildFrame() {mBike.setFrame("碳纖維車架");}
?@Overridepublic void buildSeat() {mBike.setSeat("橡膠車座");}
?@Overridepublic Bike createBike() {return mBike;}
}
?
//指揮者類
public class Director {private Builder mBuilder;
?public Director(Builder builder) {mBuilder = builder;}
?public Bike construct() {mBuilder.buildFrame();mBuilder.buildSeat();return mBuilder.createBike();}
}
?
//測(cè)試類
public class Client {public static void main(String[] args) {showBike(new OfoBuilder());showBike(new MobikeBuilder());}private static void showBike(Builder builder) {Director director = new Director(builder);Bike bike = director.construct();System.out.println(bike.getFrame());System.out.println(bike.getSeat());}
}
注意:
上面示例是 Builder模式的常規(guī)用法,指揮者類 Director 在建造者模式中具有很重要的作用,它用于指導(dǎo)具體構(gòu)建者如何構(gòu)建產(chǎn)品,控制調(diào)用先后次序,并向調(diào)用者返回完整的產(chǎn)品類,但是有些情況下需要簡(jiǎn)化系統(tǒng)結(jié)構(gòu),可以把指揮者類和抽象建造者進(jìn)行結(jié)合
// 抽象 builder 類
public abstract class Builder {
?protected Bike mBike = new Bike();
?public abstract void buildFrame();public abstract void buildSeat();public abstract Bike createBike();public Bike construct() {this.buildFrame();this.BuildSeat();return this.createBike();}
}
說(shuō)明:
這樣做確實(shí)簡(jiǎn)化了系統(tǒng)結(jié)構(gòu),但同時(shí)也加重了抽象建造者類的職責(zé),也不是太符合單一職責(zé)原則,如果construct() 過(guò)于復(fù)雜,建議還是封裝到 Director 中。
4、優(yōu)缺點(diǎn)
優(yōu)點(diǎn):
-
建造者模式的封裝性很好。使用建造者模式可以有效的封裝變化,在使用建造者模式的場(chǎng)景中,一般產(chǎn)品類和建造者類是比較穩(wěn)定的,因此,將主要的業(yè)務(wù)邏輯封裝在指揮者類中對(duì)整體而言可以取得比較好的穩(wěn)定性。
-
在建造者模式中,客戶端不必知道產(chǎn)品內(nèi)部組成的細(xì)節(jié),將產(chǎn)品本身與產(chǎn)品的創(chuàng)建過(guò)程解耦,使得相同的創(chuàng)建過(guò)程可以創(chuàng)建不同的產(chǎn)品對(duì)象。
-
可以更加精細(xì)地控制產(chǎn)品的創(chuàng)建過(guò)程 。將復(fù)雜產(chǎn)品的創(chuàng)建步驟分解在不同的方法中,使得創(chuàng)建過(guò)程更加清晰,也更方便使用程序來(lái)控制創(chuàng)建過(guò)程。
-
建造者模式很容易進(jìn)行擴(kuò)展。如果有新的需求,通過(guò)實(shí)現(xiàn)一個(gè)新的建造者類就可以完成,基本上不用修改之前已經(jīng)測(cè)試通過(guò)的代碼,因此也就不會(huì)對(duì)原有功能引入風(fēng)險(xiǎn)。符合開閉原則。
缺點(diǎn):
建造者模式所創(chuàng)建的產(chǎn)品一般具有較多的共同點(diǎn),其組成部分相似,如果產(chǎn)品之間的差異性很大,則不適合使用建造者模式,因此其使用范圍受到一定的限制。例如上面生產(chǎn)自行車的例子,現(xiàn)在如果想生產(chǎn)一臺(tái)電腦的話,兩者之間的差異較大,這個(gè)時(shí)候就不適合使用建造者模式了。
5、使用場(chǎng)景
建造者(Builder)模式創(chuàng)建的是復(fù)雜對(duì)象,其產(chǎn)品的各個(gè)部分經(jīng)常面臨著劇烈的變化,但將它們組合在一起的算法卻相對(duì)穩(wěn)定,所以它通常在以下場(chǎng)合使用。
-
創(chuàng)建的對(duì)象較復(fù)雜,由多個(gè)部件構(gòu)成,各部件面臨著復(fù)雜的變化,但構(gòu)件間的建造順序是穩(wěn)定的。
-
創(chuàng)建復(fù)雜對(duì)象的算法獨(dú)立于該對(duì)象的組成部分以及它們的裝配方式,即產(chǎn)品的構(gòu)建過(guò)程和最終的表示是獨(dú)立的。
6、模式擴(kuò)展
建造者模式除了上面的用途外,在開發(fā)中還有一個(gè)常用的使用方式,就是當(dāng)一個(gè)類構(gòu)造器需要傳入很多參數(shù)時(shí),如果創(chuàng)建這個(gè)類的實(shí)例,代碼可讀性會(huì)非常差,而且很容易引入錯(cuò)誤,此時(shí)就可以利用建造者模式進(jìn)行重構(gòu)。
重構(gòu)前代碼如下:
public class Phone {private String cpu;private String screen;private String memory;private String mainboard;
?public Phone(String cpu, String screen, String memory, String mainboard) {this.cpu = cpu;this.screen = screen;this.memory = memory;this.mainboard = mainboard;}
?public String getCpu() {return cpu;}
?public void setCpu(String cpu) {this.cpu = cpu;}
?public String getScreen() {return screen;}
?public void setScreen(String screen) {this.screen = screen;}
?public String getMemory() {return memory;}
?public void setMemory(String memory) {this.memory = memory;}
?public String getMainboard() {return mainboard;}
?public void setMainboard(String mainboard) {this.mainboard = mainboard;}
?@Overridepublic String toString() {return "Phone{" +"cpu='" + cpu + '\'' +", screen='" + screen + '\'' +", memory='" + memory + '\'' +", mainboard='" + mainboard + '\'' +'}';}
}
?
public class Client {public static void main(String[] args) {//構(gòu)建Phone對(duì)象Phone phone = new Phone("intel","三星屏幕","金士頓","華碩");System.out.println(phone);}
}
上面在客戶端代碼中構(gòu)建Phone對(duì)象,傳遞了四個(gè)參數(shù),如果參數(shù)更多呢?代碼的可讀性及使用的成本就是比較高。
重構(gòu)后代碼:
public class Phone {
?private String cpu;private String screen;private String memory;private String mainboard;
?private Phone(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 Phone build() {return new Phone(this);}}@Overridepublic String toString() {return "Phone{" +"cpu='" + cpu + '\'' +", screen='" + screen + '\'' +", memory='" + memory + '\'' +", mainboard='" + mainboard + '\'' +'}';}
}
?
public class Client {public static void main(String[] args) {Phone phone = new Phone.Builder().cpu("intel").mainboard("華碩").memory("金士頓").screen("三星").build();System.out.println(phone);}
}
重構(gòu)后的代碼在使用起來(lái)更方便,某種程度上也可以提高開發(fā)效率。從軟件設(shè)計(jì)上,對(duì)程序員的要求比較高。