---
tags: Creational Patterns
---
# 建造者模式 Builder
把一個複雜物件的建構與樣貌分離,這樣相同的建構過程可以產生不同樣貌的物件。
**例子**
像是建造房子一樣,把每個物件都分開來,之後再按照需求,將想要的房子建出來。

## 架構

### 角色
**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=閔致]