--- tags: Creational Patterns --- # 建造者模式 Builder 把一個複雜物件的建構與樣貌分離,這樣相同的建構過程可以產生不同樣貌的物件。 **例子** 像是建造房子一樣,把每個物件都分開來,之後再按照需求,將想要的房子建出來。 ![](https://i.imgur.com/ckbfZor.png) ## 架構 ![](https://i.imgur.com/yH3iWAk.png) ### 角色 **Director:** 負責指揮ConcreteBuilder該如何建構物件。 **Builder:** 用來定義建構物件過程中各必要步驟(方法)的介面。 **ConcreteBuilder:** 實作Builder介面,實際用來建構物件的類別。 **Product:** 最終要被建立出來的物件類別。 >[name=貴全] 用上面蓋房子的例子,因為建房子是有固定流程(規範builder介面),但今天要改成用其他建材(如:防火木頭)蓋房子的話,只需要在實作一個防火木頭的concretebuilder,而Director不用更改,Director最後會回傳一個Product。 >[name=閔致] ## What's difference between "Builder" and "Abstract Factory"? Generally, "Builder" focuses on its building steps order(建造的順序,不知道怎麼翻成英文), and for building complex objects, then separate the construction of a complex object from its representation, those objects have "Part of" relationship. "Abstract Factory" focuses on creating a series of related objects. 中文翻譯: Abstract Factory 和 Builder 很像,也會建構出複雜的物件,而不一樣的地方是 Builder 著重在步步建構出一個複雜物件」,Abstract Factory 則著重在「建構出一整組成品物件(不管是簡單還是複雜)」; Builder 直到最後一步才傳回成品物件,Abstract Factory 則馬上就傳回。 ### 優點 1. 在建造者模式中,客戶端不必知道產品內部組成細節。 2. 每一個具體建造者都相對獨立,與其他具體建造者無關,因此可以方便的替換具體建造者或增加新的具體建造者。 3. 可以更加精細地控制產品創建的過程。 ### 缺點 1. 建造者模式所創建的產品一般具有較多的共同點,其組成部分相似,如果產品之間的差異性很大,則不適用建造者模式,因此使用範圍受到限制。 2. 如果產品的內部變化複雜,可能會導致需要定義很多具體建造者來實現這種變化,導致系統變龐大。 >[name=YuanChangLee] ## About fluent interface Sometimes, you'll see this style used in "Builder"! ```java= public interface Builder{ //You can see the return type of these two methods is itself public Builder setComponent1(String a); public Builder setComponent2(String b); public Product Build(); } public class ConcreteBuilder implements Builder{ private String a, b; private Product product; public ConcreteBuilder(){ product = new Product(); } public Builder setComponent1(String a){ this.a = a; product.setAttributeA(a); } public Builder setComponent2(String b){ this.b = b; product.setAttributeB(b); } public Product Build(){ return product; } } public class Director{ private Builder builder; private String a, b; public Director(Builder builder, String a, String b) { this.builder = builder; this.a = a; this.b = b; } //This is fluent interface! public Product construct() { return builder.setComponent1(a) .setComponent2(b) .build(); } } ``` >[name=shao] ## 範例 HTML TXT builder Builder 類別的子類別。 * TextBuilder 類別 * HTMLBuilder 類別 Director 若使用 TextBuilder 則可產生一般格式的文件,如果使用 HTMLBuilder 則可產生 HTML 格式的文件。 ```java= public abstract class Builder { public abstract void makeTitle(String title); public abstract void makeString(String str); public abstract void makeItems(String[] items); public abstract Object getResult(); } public class HTMLBuilder extends Builder { private String filename; // 產生的檔名 private PrintWriter writer; // 寫入到檔案的PrintWriter public void makeTitle(String title) { // HTML檔的標題 filename = title + ".html"; // 根據標題決定檔名 try { writer = new PrintWriter(new FileWriter(filename)); // 建立PrintWriter } catch (IOException e) { e.printStackTrace(); } writer.println("<html><head><title>" + title + "</title></head><body>"); // 輸出標題 writer.println("<h1>" + title + "</h1>"); } public void makeString(String str) { // HTML檔的字串 writer.println("<p>" + str + "</p>"); // 以<p>標籤輸出 } public void makeItems(String[] items) { // HTML檔的項目 writer.println("<ul>"); // 以<ul>和<li>輸出 for (int i = 0; i < items.length; i++) { writer.println("<li>" + items[i] + "</li>"); } writer.println("</ul>"); } public Object getResult() { // 完成的文件 writer.println("</body></html>"); // 關閉標籤 writer.close(); // 關閉檔案 return filename; // 傳回檔名 } } public class TextBuilder extends Builder { private StringBuffer buffer = new StringBuffer(); // 開始在此欄位建立文件,用 StringBuffer 速度較快 public void makeTitle(String title) { // 一般文字格式的標題 buffer.append("==============================\n"); // 花邊 buffer.append("『" + title + "』\n"); // 有『』的標題 buffer.append("\n"); // 空行 } public void makeString(String str) { // 一般文字格式的字串 buffer.append('■' + str + "\n"); // 有■的字串 buffer.append("\n"); // 空行 } public void makeItems(String[] items) { // 一般文字格式的項目 for (int i = 0; i < items.length; i++) { buffer.append("‧" + items[i] + "\n"); // 有‧的項目 } buffer.append("\n"); // 空行 } public Object getResult() { // 完成的文件 buffer.append("==============================\n"); // 花邊 return buffer.toString(); // 把StringBuffer轉換成String } } public class Director { //並不需要知道傳進來的是哪一個 ConcreteBuilder private Builder builder; public Director(Builder builder) { // 先把Builder的子類別的個體, this.builder = builder; // 儲存在builder欄位 } public Object construct() { // 建立文件 builder.makeTitle("Greeting"); // 標題 builder.makeString("從早上到白天結束"); // 字串 builder.makeItems(new String[]{ // 項目 "早安。", "午安。", }); builder.makeString("到了晚上"); // 另一個字串 builder.makeItems(new String[]{ // 另一個項目 "晚安。", "祝你有個好夢。", "再見。", }); return builder.getResult(); // 完成的文件就是傳回值 } } public class Main { public static void main(String[] args) { if (args.length != 1) { usage(); System.exit(0); } if (args[0].equals("plain")) { Director director = new Director(new TextBuilder()); //只呼叫 construct 方法 String result = (String)director.construct(); System.out.println(result); } else if (args[0].equals("html")) { Director director = new Director(new HTMLBuilder()); String filename = (String)director.construct(); System.out.println("已產生" + filename + "。"); } else { usage(); System.exit(0); } } public static void usage() { System.out.println("Usage: java Main plain 產生一般格式的文件"); System.out.println("Usage: java Main html 產生HTML格式的文件"); } } ``` >[name=閔致]