在軟件開發過程中有時需要創建一個複雜的對象,這個複雜對象通常由多個子部件按一定的步驟組合而成。例如,計算機是由 OPU、主板、內存、硬盤、顯卡、機箱、顯示器、鍵盤、鼠標等部件組裝而成的,採購員不可能自己去組裝計算機,而是將計算機的配置要求告訴計算機銷售公司,計算機銷售公司安排技術人員去組裝計算機,然後再交給要買計算機的採購員。
生活中這樣的例子很多,如遊戲中的不同角色,其性別、個性、能力、臉型、體型、服裝、髮型等特性都有所差異;還有汽車中的方向盤、發動機、車架、輪胎等部件也多種多樣;每封電子郵件的發件人、收件人、主題、內容、附件等內容也各不相同。
以上所有這些產品都是由多個部件構成的,各個部件可以靈活選擇,但其創建步驟都大同小異。這類產品的創建無法用前面介紹的工廠模式描述,只有建造者模式可以很好地描述該類產品的創建。
建造者模式的定義與特點
建造者(Builder
)模式的定義:
指將一個複雜對象的構造與它的表示分離,使同樣的構建過程可以創建不同的表示,這樣的設計模式被稱爲建造者模式
。它是將一個複雜的對象分解爲多個簡單的對象,然後一步一步構建而成。它將變與不變相分離,即產品的組成部分是不變的,但每一部分是可以靈活選擇的。
該模式的主要優點如下:
1.各個具體的建造者相互獨立,有利於系統的擴展。
2.客戶端不必知道產品內部組成的細節,便於控制細節風險。
其缺點如下:
1.產品的組成部分必須相同,這限制了其使用範圍。
2.如果產品的內部變化複雜,該模式會增加很多的建造者類。
建造者模式的結構
建造者(Builder
)模式由產品
、抽象建造者
、具體建造者
、指揮者
等 4 個要素構成,如下:
1.產品角色(Product):它是包含多個組成部件的複雜對象,由具體建造者來創建其各個部件。
2.抽象建造者(Builder):它是一個包含創建產品各個子部件的抽象方法的接口,通常還包含一個返回複雜產品的方法 。
3.具體建造者(Concrete Builder):實現 Builder 接口,完成複雜產品的各個部件的具體創建方法。
4.指揮者(Director):它調用建造者對象中的部件構造與裝配方法完成複雜對象的創建,在指揮者中不涉及具體產品的信息。
建造者模式的實現
用建造者(Builder)模式描述手機:
對照上邊抽象工廠模式的結構:
1.產品角色(Phone ):包含多個組成部件的複雜對象
/**
* 產品:手機
* 使用lombok插件省去get,set,toString。
*/
@Date
public class Phone {
private String screen;//屏幕
private String shell;//外殼
private String cpu;//處理器
}
2.抽象建造者(AbstractWorker ):包含創建產品各個子部件的抽象方法的接口
/**
* 抽象建造者:造手機的工人
*/
public interface AbstractWorker {
AbstractWorker buildScreen();//造屏幕
AbstractWorker buildShell();//造外殼
AbstractWorker buildCpu();//造處理器
Phone build();//組裝產品
}
3.具體建造者:實現 AbstractWorker 接口
/**
* 具體建造者:造華爲手機的工人
*/
public class HWWorker implements AbstractWorker {
private Phone hwPhone = new Phone();
public AbstractWorker buildScreen() {
hwPhone.setScreen("華爲的屏幕");
return this;
}
public AbstractWorker buildShell() {
hwPhone.setShell("華爲的外殼");
return this;
}
public AbstractWorker buildCpu() {
hwPhone.setCpu("華爲的cpu");
return this;
}
public Phone build() {
return hwPhone;
}
}
/**
* 具體建造者:造小米手機的工人
*/
public class XMWorker implements AbstractWorker{
private Phone xmPhone = new Phone();
public AbstractWorker buildScreen() {
xmPhone.setScreen("小米的屏幕");
return this;
}
public AbstractWorker buildShell() {
xmPhone.setShell("小米的外殼");
return this;
}
public AbstractWorker buildCpu() {
xmPhone.setCpu("小米的cpu");
return this;
}
public Phone build() {
return xmPhone;
}
}
4.指揮者(Director):調用建造者對象中的部件構造與裝配方法完成複雜對象的創建
/**
* 指揮者
*/
public class Director {
private AbstractWorker builder;
//構造方法:給我華爲的工人就構造一個華爲的指揮者
public Director(AbstractWorker builder) {
this.builder = builder;
}
//產品構建與組裝方法:指揮工人幹活
public Phone builder(){
return builder.buildScreen()
.buildShell()
.buildCpu()
.build();
}
}
5.測試類:
public class BuilderPatternTest {
@Test
public void test(){
showPhone(new HWWorker());
showPhone(new XMWorker());
}
private void showPhone(AbstractWorker builder){
Director director = new Director(builder);//根據傳入的工人構建不同的指揮者
Phone phone = director.builder();//指揮工人幹活:造手機
System.out.println(phone);
}
}
6.測試結果:
華爲的工人造華爲的手機,小米的工人造小米手機
類結構圖:
應用場景
建造者(Builder)模式創建的是複雜對象,其產品的各個部分經常面臨着劇烈的變化,但將它們組合在一起的算法卻相對穩定,所以它通常在以下場合使用:
1.創建的對象較複雜,由多個部件構成,各部件面臨着複雜的變化,但構件間的建造順序是穩定的。
2.創建複雜對象的算法獨立於該對象的組成部分以及它們的裝配方式,即產品的構建過程和最終的表示是獨立的。
建造者模式和工廠模式的區別
1.建造者模式更加註重方法的調用順序,工廠模式注重創建對象。
2.創建對象的力度不同,建造者模式創建複雜的對象,由各種複雜的部件組成,工廠模式創建出來的都一樣。
3.關注的重點不同,工廠模式只需要把對象創建出來,而建造者不僅要創建出來,還要知道這個對象由哪些部件組成。
4.建造者模式根據建造過程中的順序不一樣,最終的對象部件組成也不一樣。
建造者模式的擴展
建造者(Builder)模式在應用過程中可以根據需要改變,如果創建的產品種類只有一種,只需要一個具體建造者,這時可以省略掉抽象建造者,甚至可以省略掉指揮者角色。比如:
/**
* 產品:電腦
*/
public class Computer {
private String screen;//屏幕
private String shell;//外殼
private String cpu;//處理器
/**
* 造電腦的工人
*/
public class Worker {
private Computer computer = new Computer();
public Worker buildScreen(String screen) {
computer.setScreen(screen);
return this;
}
public Worker buildShell(String shell) {
computer.setShell(shell);
return this;
}
public Worker buildCpu(String cpu) {
computer.setCpu(cpu);
return this;
}
public Computer build() {
return computer;
}
}
//測試代碼
@Test
public void test2 (){
Computer computer = new Worker().buildScreen("4K屏幕")
.buildShell("堅硬的外殼")
.buildCpu("高端處理器")
.build();
System.out.println(computer);
}
運行結果:
上面的示例代碼只是傳入三個參數,如果構造這個對象要13個參數呢,更多的參數呢,builder 模式的優勢將會更加明顯,傳遞參數更加靈活,代碼具有更高的可讀性。