Builder Pattern(建造者模式)

是什麼?

Builder Pattern 將一個複雜物件的建構過程和它的表示分離。透過同樣的建構步驟,可以產出不同的物件表示。特別適合那種有很多可選參數或需要分步驟組裝的物件。

ℹ️GoF 分類

Builder 屬於建立型模式(Creational Pattern),重點在於將複雜物件的建構過程標準化,並允許靈活配置。

什麼時候用?

什麼時候不該用?

⚠️過度設計警告

如果物件很簡單(只有 2-3 個必要參數),直接用建構子或工廠方法就好。Builder 會多出 Builder 類別,只有在物件真的很複雜或參數很多且大部分可選時才值得用。

執行流程

1

定義產品

決定最終要建立的複雜物件結構

2

定義 Builder 介面

宣告每個建構步驟的方法

3

實作具體 Builder

實作每個步驟,內部累積狀態

4

鏈式呼叫

依需求呼叫各步驟方法(Fluent API)

5

Build

呼叫 Build() 取得最終產品

流程解讀:Builder 把「一次性的複雜建構」拆成「多步驟的漸進配置」。每個 setter 方法只設定一個面向,回傳 this 支援 Fluent API(流暢介面,指每個方法回傳自身以支援鏈式呼叫)。Builder 內部累積所有設定,直到 Build() 被呼叫時才一次性組裝最終產品。這確保了產品在建立完成前不會處於不完整狀態。

程式碼範例

C# 版本

csharp
// 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: 5000ms

TypeScript 版本

typescript
// 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 版本

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: 5000ms

Java 版本

java
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
configures step by step
HttpRequestBuilder
build()
HttpRequest (Product)
Director (optional)

結構解讀:Client 透過 Builder 的 Fluent API 逐步配置(SetUrl().SetMethod().AddHeader()),最後呼叫 Build() 取得完成品。Director 是可選角色(GoF 原版有,現代實務常省略),負責預定義常用的建構步驟組合。Builder 內部累積狀態,Build() 時一次組裝成最終的 Product,確保 Product 建立後的一致性。

實戰補充

💡資深開發者經驗

Fluent API:現代 Builder 最常見的形式就是 Fluent API — 每個設定方法回傳 this,支援鏈式呼叫。C# 的 IHostBuilderIServiceCollection、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 類別,簡單物件不需要 |

你可能也想看

Abstract Factory PatternPrototype Pattern

按 ← → 鍵切換課程