一 引言
說明:如果想要直接閱讀定義等理論內容,可以直接跳轉到第二大點
在生活中有很多場景與我們今天要說的 “建造者模式” 是非常匹配的,打個比方一臺計算機是由 CPU、記憶體、顯示卡、記憶體、滑鼠、鍵盤、顯示器等等內容組合而成的,我們想要一臺電腦,我們不會可能自己去做這些配件,一般都是通過告訴銷售公司,然後其派生產技術人員給你做好指定的配件。
先不管,誰買,誰做,誰管理的問題,我們可以分析得到,建造電腦的這個 “過程” 是穩定的也就是說,不管什麼配置的電腦,這些配件都是必須要有的,只是具體的細節不一樣,例如你的配置更好,他的差一些
但是,我作為一個買家,我並不想管這些,我只告訴你,我要一臺中等配置的電腦,你負責“建造”好給我就行了,這就是建造者模式比較通俗的講法
下面我們通過這個計算機的例子,循序漸進的看一下:
二 通過例子循序漸進認識建造者模式
首先,不管怎麼建,怎麼買,一個 Computer 電腦類是必須的,我們隨便挑選三個元件來進行演示,CPU、記憶體、顯示器,補充其 get set toString 方法
/**
* 產品:電腦
*/
public class Computer {
private String cpu; // CPU
private String graphicsCard; // 記憶體
private String displayScreen; // 顯示器
public String getCpu() {
return cpu;
}
public void setCpu(String cpu) {
this.cpu = cpu;
}
public String getGraphicsCard() {
return graphicsCard;
}
public void setGraphicsCard(String graphicsCard) {
this.graphicsCard = graphicsCard;
}
public String getDisplayScreen() {
return displayScreen;
}
public void setDisplayScreen(String displayScreen) {
this.displayScreen = displayScreen;
}
@Override
public String toString() {
return "Computer{" +
"cpu='" + cpu + '\'' +
", graphicsCard='" + graphicsCard + '\'' +
", displayScreen='" + displayScreen + '\'' +
'}';
}
}
(一) 最簡單直白的方式(不好)
這種方法基本可以說沒什麼技術含量了,直接 new + set 就行了
public class Test {
public static void main(String[] args) {
Computer computer = new Computer();
computer.setCpu("英特爾酷睿 i5 處理器");
computer.setGraphicsCard("4g記憶體");
computer.setDisplayScreen("14寸 1080p 60hz顯示器");
System.out.println(computer.toString());
}
}
列印一下結果:
Computer{cpu='英特爾酷睿 i5 處理器', graphicsCard='4g', displayScreen='14寸 1080p 60hz顯示器'}
(二) 客戶直接聯絡生產技術人員
上面這種方式,不用說也知道不合適了,所以技術人員(建造者)他來了!但是不同的技術人員會製作的配件也不一樣,例如有的會做 144hz 的顯示器,而有的專攻 60hz 的顯示器,有高低配置,不同型號之分
為此我們為其抽象出一個 ComputerBuilder 的抽象類
/**
* 電腦的建造者
*/
public abstract class ComputerBuilder {
abstract void buildCpu(); // 建造CPU
abstract void buildGraphicsCard(); // 建造記憶體
abstract void buildDisplayScreen(); // 建造顯示器
abstract Computer getComputer(); // 拿到這臺電腦
}
下面就來寫建造者的具體實現,例如先寫一個低配置電腦建造的實現
/**
* 低配置電腦
*/
public class LowConfigurationComputerBuilder extends ComputerBuilder {
private Computer computer;
public LowConfigurationComputerBuilder(){
computer = new Computer();
}
@Override
void buildCpu() {
computer.setCpu("英特爾酷睿 i5 處理器");
System.out.println("buildCpu: 英特爾酷睿 i5 處理器");
}
@Override
void buildGraphicsCard() {
computer.setGraphicsCard("8g記憶體");
System.out.println("buildGraphicsCard: 8g記憶體");
}
@Override
void buildDisplayScreen() {
computer.setDisplayScreen("1080p 60hz顯示器");
System.out.println("buildDisplayScreen: 1080p 60hz顯示器");
}
@Override
Computer getComputer() {
return computer;
}
}
測試一下:
public class Test {
public static void main(String[] args) {
// 建立低配置電腦建造者
LowConfigurationComputerBuilder builder = new LowConfigurationComputerBuilder();
builder.buildCpu();
builder.buildGraphicsCard();
builder.buildDisplayScreen();
Computer computer = builder.getComputer();
System.out.println("建造出的電腦: " + computer );
}
}
執行結果:
buildCpu: 英特爾酷睿 i5 處理器
buildGraphicsCard: 8g記憶體
buildDisplayScreen: 1080p 60hz顯示器
建造出的電腦: Computer{cpu='英特爾酷睿 i5 處理器', graphicsCard='8g記憶體', displayScreen='1080p 60hz顯示器'}
(三) 客戶聯絡銷售公司
雖然上面的方法是比第一種強一些,但是客戶自己去聯絡生產技術人員,顯然不是很合理,正常的做法,我們都是先去聯絡銷售公司,告訴他們我想要什麼配置的電腦就可以了,細節我並不想管
public class SalesCompany {
public Computer buildComputer(ComputerBuilder builder){
builder.buildCpu();
builder.buildGraphicsCard();
builder.buildDisplayScreen();
return builder.getComputer();
}
}
測試程式碼
public class Test {
public static void main(String[] args) {
// 建立低配置電腦建造者
LowConfigurationComputerBuilder builder = new LowConfigurationComputerBuilder();
// 建立電腦銷售中心
SalesCompany salesCompany = new SalesCompany();
// 指定具體的電腦建造者去完成 電腦 這個產品
Computer computer = salesCompany.buildComputer(builder);
System.out.println("建造出的電腦: " + computer );
}
}
現在程式碼已經比較完善了,也就是我們買家通過聯絡電腦銷售中心,然後銷售中心去呼叫使用者想要的配置的建造者,剛才我們建立的是一個“低配置電腦建造者”,如果我們想要換成中等配置的電腦,該怎麼做呢?
現在只需要增加一箇中等配置電腦建造者,實現Builder抽象類就可以了
/**
* 低配置電腦
*/
public class MiddleConfigurationComputerBuilder extends ComputerBuilder {
private Computer computer;
public MiddleConfigurationComputerBuilder(){
computer = new Computer();
}
@Override
void buildCpu() {
computer.setCpu("英特爾酷睿 i7 處理器");
System.out.println("buildCpu: 英特爾酷睿 i7 處理器");
}
@Override
void buildGraphicsCard() {
computer.setGraphicsCard("16g記憶體");
System.out.println("buildGraphicsCard: 16g記憶體");
}
@Override
void buildDisplayScreen() {
computer.setDisplayScreen("2k 144hz顯示器");
System.out.println("buildDisplayScreen: 2k 60hz顯示器");
}
@Override
Computer getComputer() {
return computer;
}
}
測試一下
public class Test {
public static void main(String[] args) {
MiddleConfigurationComputerBuilder builder = new MiddleConfigurationComputerBuilder();
// 建立電腦銷售中心
SalesCompany salesCompany = new SalesCompany();
// 指定具體的電腦建造者去完成 電腦 這個產品
Computer computer = salesCompany.buildComputer(builder);
System.out.println("建造出的電腦: " + computer );
}
}
執行結果
buildCpu: 英特爾酷睿 i7 處理器
buildGraphicsCard: 16g記憶體
buildDisplayScreen: 2k 60hz顯示器
建造出的電腦: Computer{cpu='英特爾酷睿 i7 處理器', graphicsCard='16g記憶體', displayScreen='2k 144hz顯示器'}
其實到這裡一個建造者模式的例項就寫完了,下面我們結合概念,來深入理解一下建造者模式
三 建造者模式
(一) 概念
建造者模式:將一個複雜物件的構建與它的表示分離,使得同樣的構建過程可以建立不同的表示
-
也就是說,產品的生成過程或者說組成,是不變的,而每一部分都是可以自行選擇的,即其內部表象是可以變化的,這也就是所說的變與不變相分離
-
此種情況下,使用者只需要指定建造的型別就可以得到他們,而具體的過程和細節就不需要知道了
(二) 優缺點
首先,此模式封裝性好,構建和表示進行了分離,每一個建造者都是相互獨立的,利於解耦和擴充套件,符合 “開閉原則” 同時客戶呼叫時,不需要知道產品細節
但是也正是因為產品生成過程這個不變的部分,限制了它的使用範圍,同時如果產品內部日後發生什麼改變,則建造者也得同樣修改,維護成本不小
(三) 結構
根據上面的結構圖,我們分別說明一下其中的四個角色(除 Client 呼叫者以外)
- Product(產品角色):多個元件構成的複雜物件,即上述例子中的電腦
- Builder(抽象建造者):一個包含建立產品各個子部件的抽象方法的介面/抽象類,一般還包含一個返回結果的方法,如上述中的 ComputerBuilder 類
- ConcreteBuilder(具體建造者):Builder 的具體實現類,如上述中具體的 低配置電腦建造者 和 中等配置電腦建造者
- Director(指揮者):呼叫建造者物件中的部件構造與裝配方法,以建立一個複雜物件
- 指揮者中不含具體產品資訊
- 其隔離了客戶與物件的生產過程
(四) 適用場景
- 順序會對同一方法的結果產生影響,例如建房子,應當先打地基,再架鋼筋水泥
- 同一個物件可以裝配不同的部件或者零件,同時結果不同
- 產品類有複雜的內部結構,且這些產品物件具有共性