Builder Pattern(建造者模式)
是什麼?
Builder Pattern 將一個複雜物件的建構過程和它的表示分離。透過同樣的建構步驟,可以產出不同的物件表示。特別適合那種有很多可選參數或需要分步驟組裝的物件。
ℹ️GoF 分類
Builder 屬於建立型模式(Creational Pattern),重點在於將複雜物件的建構過程標準化,並允許靈活配置。
什麼時候用?
- 建構子參數超過 4-5 個,且很多是可選的
- 物件的建立需要多個步驟,順序可能不同
- 你想用同樣的建構流程產出不同的表示(例如 HTML 報表和 PDF 報表)
什麼時候不該用?
⚠️過度設計警告
如果物件很簡單(只有 2-3 個必要參數),直接用建構子或工廠方法就好。Builder 會多出 Builder 類別,只有在物件真的很複雜或參數很多且大部分可選時才值得用。
執行流程
定義產品
決定最終要建立的複雜物件結構
定義 Builder 介面
宣告每個建構步驟的方法
實作具體 Builder
實作每個步驟,內部累積狀態
鏈式呼叫
依需求呼叫各步驟方法(Fluent API)
Build
呼叫 Build() 取得最終產品
流程解讀:Builder 把「一次性的複雜建構」拆成「多步驟的漸進配置」。每個 setter 方法只設定一個面向,回傳 this 支援 Fluent API(流暢介面,指每個方法回傳自身以支援鏈式呼叫)。Builder 內部累積所有設定,直到 Build() 被呼叫時才一次性組裝最終產品。這確保了產品在建立完成前不會處於不完整狀態。
程式碼範例
C# 版本
// 1. 產品
public class HttpRequest
{
public string Url { get; set; } = "";
public string Method { get; set; } = "GET";
public Dictionary<string, string> Headers { get; set; } = new();
public string? Body { get; set; }
public int TimeoutMs { get; set; } = 30000;
public override string ToString() =>
$"{Method} {Url} | Headers: {Headers.Count} | Timeout: {TimeoutMs}ms";
}
// 2. Builder(Fluent API)
public class HttpRequestBuilder
{
private readonly HttpRequest _request = new();
public HttpRequestBuilder SetUrl(string url)
{
_request.Url = url;
return this;
}
public HttpRequestBuilder SetMethod(string method)
{
_request.Method = method;
return this;
}
public HttpRequestBuilder AddHeader(string key, string value)
{
_request.Headers[key] = value;
return this;
}
public HttpRequestBuilder SetBody(string body)
{
_request.Body = body;
return this;
}
public HttpRequestBuilder SetTimeout(int ms)
{
_request.TimeoutMs = ms;
return this;
}
public HttpRequest Build() => _request;
}
// 3. 使用
var request = new HttpRequestBuilder()
.SetUrl("https://api.example.com/users")
.SetMethod("POST")
.AddHeader("Content-Type", "application/json")
.AddHeader("Authorization", "Bearer token123")
.SetBody("{\"name\": \"Alice\"}")
.SetTimeout(5000)
.Build();
Console.WriteLine(request); // POST https://api.example.com/users | Headers: 2 | Timeout: 5000msTypeScript 版本
// 1. 產品
interface HttpRequest {
url: string;
method: string;
headers: Record<string, string>;
body?: string;
timeoutMs: number;
}
// 2. Builder
class HttpRequestBuilder {
private url = "";
private method = "GET";
private headers: Record<string, string> = {};
private body?: string;
private timeoutMs = 30000;
setUrl(url: string): this {
this.url = url;
return this;
}
setMethod(method: string): this {
this.method = method;
return this;
}
addHeader(key: string, value: string): this {
this.headers[key] = value;
return this;
}
setBody(body: string): this {
this.body = body;
return this;
}
setTimeout(ms: number): this {
this.timeoutMs = ms;
return this;
}
build(): HttpRequest {
return {
url: this.url,
method: this.method,
headers: { ...this.headers },
body: this.body,
timeoutMs: this.timeoutMs,
};
}
}
// 3. 使用
const request = new HttpRequestBuilder()
.setUrl("https://api.example.com/users")
.setMethod("POST")
.addHeader("Content-Type", "application/json")
.addHeader("Authorization", "Bearer token123")
.setBody('{"name": "Alice"}')
.setTimeout(5000)
.build();
console.log(request);Python 版本
from dataclasses import dataclass, field
# 1. 產品
@dataclass
class HttpRequest:
url: str = ""
method: str = "GET"
headers: dict = field(default_factory=dict)
body: str | None = None
timeout_ms: int = 30000
def __str__(self) -> str:
return f"{self.method} {self.url} | Headers: {len(self.headers)} | Timeout: {self.timeout_ms}ms"
# 2. Builder
class HttpRequestBuilder:
def __init__(self):
self._request = HttpRequest()
def set_url(self, url: str) -> "HttpRequestBuilder":
self._request.url = url
return self
def set_method(self, method: str) -> "HttpRequestBuilder":
self._request.method = method
return self
def add_header(self, key: str, value: str) -> "HttpRequestBuilder":
self._request.headers[key] = value
return self
def set_body(self, body: str) -> "HttpRequestBuilder":
self._request.body = body
return self
def set_timeout(self, ms: int) -> "HttpRequestBuilder":
self._request.timeout_ms = ms
return self
def build(self) -> HttpRequest:
return self._request
# 3. 使用
request = (
HttpRequestBuilder()
.set_url("https://api.example.com/users")
.set_method("POST")
.add_header("Content-Type", "application/json")
.add_header("Authorization", "Bearer token123")
.set_body('{"name": "Alice"}')
.set_timeout(5000)
.build()
)
print(request) # POST https://api.example.com/users | Headers: 2 | Timeout: 5000msJava 版本
import java.util.HashMap;
import java.util.Map;
// 1. 產品
public class HttpRequest {
private final String url;
private final String method;
private final Map<String, String> headers;
private final String body;
private final int timeoutMs;
private HttpRequest(Builder builder) {
this.url = builder.url;
this.method = builder.method;
this.headers = new HashMap<>(builder.headers);
this.body = builder.body;
this.timeoutMs = builder.timeoutMs;
}
@Override
public String toString() {
return method + " " + url + " | Headers: " + headers.size() + " | Timeout: " + timeoutMs + "ms";
}
// 2. 內部靜態 Builder
public static class Builder {
private String url = "";
private String method = "GET";
private Map<String, String> headers = new HashMap<>();
private String body;
private int timeoutMs = 30000;
public Builder setUrl(String url) { this.url = url; return this; }
public Builder setMethod(String method) { this.method = method; return this; }
public Builder addHeader(String k, String v) { this.headers.put(k, v); return this; }
public Builder setBody(String body) { this.body = body; return this; }
public Builder setTimeout(int ms) { this.timeoutMs = ms; return this; }
public HttpRequest build() { return new HttpRequest(this); }
}
}
// 3. 使用
HttpRequest request = new HttpRequest.Builder()
.setUrl("https://api.example.com/users")
.setMethod("POST")
.addHeader("Content-Type", "application/json")
.addHeader("Authorization", "Bearer token123")
.setBody("{\"name\": \"Alice\"}")
.setTimeout(5000)
.build();
System.out.println(request);結構圖
結構解讀:Client 透過 Builder 的 Fluent API 逐步配置(SetUrl().SetMethod().AddHeader()),最後呼叫 Build() 取得完成品。Director 是可選角色(GoF 原版有,現代實務常省略),負責預定義常用的建構步驟組合。Builder 內部累積狀態,Build() 時一次組裝成最終的 Product,確保 Product 建立後的一致性。
實戰補充
💡資深開發者經驗
Fluent API:現代 Builder 最常見的形式就是 Fluent API — 每個設定方法回傳 this,支援鏈式呼叫。C# 的 IHostBuilder、IServiceCollection、Entity Framework 的 ModelBuilder 都是這種風格。
Configuration Builder:.NET 的 ConfigurationBuilder().AddJsonFile().AddEnvironmentVariables().Build() 就是 Builder Pattern 的完美應用。
不可變物件:Builder 搭配不可變物件(Immutable Object)特別好用。Builder 負責收集所有參數,Build() 時一次建立不可變的最終產品。Java 的 StringBuilder 也是這個概念。
Director 角色:GoF 原始定義有個 Director 角色負責編排建構步驟。現代實務中 Director 通常被省略,直接由 Client 呼叫 Builder。
理解測驗
🤔 Builder Pattern 最主要解決什麼問題?
🤔 Fluent API 風格的 Builder 為什麼每個方法都 return this?
🤔 以下哪個場景最適合用 Builder Pattern?
面試常見問題
Q: Builder Pattern 和 Telescoping Constructor(伸縮建構子)有什麼差別?
A: Telescoping Constructor 指為了處理可選參數,建立多個建構子多載(2 參數、3 參數、4 參數...),參數一多就難以維護且容易搞混順序。Builder 用具名方法逐步設定,每個方法名稱就是參數的語意,不會搞混。判斷標準:建構子參數超過 4 個且有可選參數 → 用 Builder。
Q: Builder 和 Factory 有什麼差別?
A: Factory 關注「建立哪種物件」(選擇具體類別),Builder 關注「怎麼建構這個物件」(逐步配置複雜的參數)。Factory 回傳的物件通常一步到位,Builder 需要多步配置後才 Build()。
相關模式
| 模式 | 關係 |
|------|------|
| Abstract Factory | 都是建立型模式,但 Abstract Factory 一步建立(選類別),Builder 多步配置(填參數) |
| Prototype | 都能建立複雜物件,但 Prototype 透過複製既有物件,Builder 透過步驟化建構 |
| Composite | Builder 常用來建構複雜的 Composite 結構,因為手動組裝樹狀結構容易出錯 |
| Fluent Interface | Builder 最常用的實作風格,每個方法回傳 this 支援鏈式呼叫 |
重點整理
💡一句話記住
Builder Pattern = 點餐流程:一步步選配,最後結帳取餐。 口訣:「參數多用 Builder,鏈式呼叫最清楚」
| 概念 | 說明 | |------|------| | Builder(建造者) | 提供逐步配置的方法 | | Product(產品) | 最終被建立的複雜物件 | | Director(指揮者) | 編排建構步驟(現代實務常省略) | | Fluent API | 每個方法回傳 this,支援鏈式呼叫 | | 核心好處 | 清晰的建構過程 + 可選參數不再混亂 | | 代價 | 多了 Builder 類別,簡單物件不需要 |