---
# System prepended metadata

title: 建造者模式 Builder
tags: [Creational Patterns]

---

---
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=閔致]