1. 程式人生 > >【設計模式】第四篇:建造者模式也沒那麼難

【設計模式】第四篇:建造者模式也沒那麼難

![](//p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/3c68df3e1ac44ba68c613b630fa5c43f~tplv-k3u1fbpfcp-zoom-1.image) # 一 引言 說明:如果想要直接閱讀定義等理論內容,可以直接跳轉到第二大點 在生活中有很多場景與我們今天要說的 “建造者模式” 是非常匹配的,打個比方一臺計算機是由 CPU、記憶體、顯示卡、記憶體、滑鼠、鍵盤、顯示器等等內容組合而成的,我們想要一臺電腦,我們不會可能自己去做這些配件,一般都是通過告訴銷售公司,然後其派生產技術人員給你做好指定的配件。 先不管,誰買,誰做,誰管理的問題,我們可以分析得到,建造電腦的這個 **“過程” 是穩定的**也就是說,不管什麼配置的電腦,這些配件都是必須要有的,只是**具體的細節不一樣**,例如你的配置更好,他的差一些 但是,我作為一個買家,我並不想管這些,我只告訴你,我要一臺中等配置的電腦,你負責“建造”好給我就行了,這就是建造者模式比較通俗的講法 下面我們通過這個計算機的例子,循序漸進的看一下: # 二 通過例子循序漸進認識建造者模式 首先,不管怎麼建,怎麼買,一個 Computer 電腦類是必須的,我們隨便挑選三個元件來進行演示,CPU、記憶體、顯示器,補充其 get set toString 方法 ```java /** * 產品:電腦 */ 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 就行了 ```java 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 的抽象類 ```java /** * 電腦的建造者 */ public abstract class ComputerBuilder { abstract void buildCpu(); // 建造CPU abstract void buildGraphicsCard(); // 建造記憶體 abstract void buildDisplayScreen(); // 建造顯示器 abstract Computer getComputer(); // 拿到這臺電腦 } ``` 下面就來寫建造者的具體實現,例如先寫一個低配置電腦建造的實現 ```java /** * 低配置電腦 */ 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; } } ``` 測試一下: ```java 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顯示器'} ## (三) 客戶聯絡銷售公司 雖然上面的方法是比第一種強一些,但是客戶自己去聯絡生產技術人員,顯然不是很合理,正常的做法,我們都是先去聯絡銷售公司,告訴他們我想要什麼配置的電腦就可以了,細節我並不想管 ```java public class SalesCompany { public Computer buildComputer(ComputerBuilder builder){ builder.buildCpu(); builder.buildGraphicsCard(); builder.buildDisplayScreen(); return builder.getComputer(); } } ``` 測試程式碼 ```java 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抽象類就可以了 ```java /** * 低配置電腦 */ 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; } } ``` 測試一下 ```java 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顯示器'} 其實到這裡一個建造者模式的例項就寫完了,下面我們結合概念,來深入理解一下建造者模式 # 三 建造者模式 ## (一) 概念 **建造者模式:將一個複雜物件的構建與它的表示分離,使得同樣的構建過程可以建立不同的表示** - 也就是說,**產品的生成過程**或者說**組成**,是**不變**的,而每一部分都是可以自行選擇的,即其**內部表象是可以變化**的,這也就是所說的變與不變相分離 - 此種情況下,使用者只需要指定建造的型別就可以得到他們,而具體的過程和細節就不需要知道了 ## (二) 優缺點 首先,此模式封裝性好,構建和表示進行了分離,每一個建造者都是相互獨立的,利於解耦和擴充套件,符合 “開閉原則” 同時客戶呼叫時,不需要知道產品細節 但是也正是因為產品生成過程這個不變的部分,限制了它的使用範圍,同時如果產品內部日後發生什麼改變,則建造者也得同樣修改,維護成本不小 ## (三) 結構 ![](//p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/d3a363870df94b169661938f481ac77f~tplv-k3u1fbpfcp-zoom-1.image) 根據上面的結構圖,我們分別說明一下其中的四個角色(除 Client 呼叫者以外) - Product(產品角色):多個元件構成的複雜物件,即上述例子中的電腦 - Builder(抽象建造者):一個包含建立產品各個子部件的抽象方法的介面/抽象類,一般還包含一個返回結果的方法,如上述中的 ComputerBuilder 類 - ConcreteBuilder(具體建造者):Builder 的具體實現類,如上述中具體的 低配置電腦建造者 和 中等配置電腦建造者 - Director(指揮者):呼叫建造者物件中的部件構造與裝配方法,以建立一個複雜物件 - 指揮者中不含具體產品資訊 - 其隔離了客戶與物件的生產過程 ## (四) 適用場景 - 順序會對同一方法的結果產生影響,例如建房子,應當先打地基,再架鋼筋水泥 - 同一個物件可以裝配不同的部件或者零件,同時結果不同 - 產品類有複雜的內部結構,且這些產品物件具