進行 iOS 開發時,你可能曾在別人的程式碼中看到 init(...) 而不是 static func make(...),感到疑惑。
先說結論。
Swift 靜態工廠方法能替建立流程命名、彈性調整回傳型別,還能控制物件重用;當只用 init 不夠靈活時,就可以派上用場。
今天從實務角度逐一說明為什麼用 static func make 取代 init。
本文的靜態工廠只是在名稱上類似 GoF Factory Method Pattern;該模式由子類別決定建立方式。更多 Swift 實作範例請見 使用工廠函式分離建立邏輯的方法,GoF 模式的界線則請見 工廠方法 vs. 抽象工廠。
靜態工廠方法究竟是什麼?
名稱聽起來很厲害,但概念很簡單。
這是會建立並回傳物件的 static 方法。不直接呼叫建構子,而是加上一個包住建立流程的函式。
大致如下。
struct Button {
let title: String
let style: Style
// init外包一層的靜態工廠方法
static func makePrimary(title: String) -> Button {
Button(title: title, style: .primary)
}
}
呼叫端會像這樣:Button.makePrimary(title: "확인")。
結果和直接呼叫 Button(title:style:) 相同,只是從名稱就能看出「要建立哪個按鈕」。
這個小差異在實務上影響不小。
改用 make 取代 init 的 4 個理由
以下是我在實際專案中感受到的優點。
1. 用名稱表達意圖
建構子的名稱全部都是 init,只能靠參數組合區分。
因此,類似的 init 一多就容易混淆。加上 make(fromJSON:)、make(withDefaults:) 這類名稱後,只看呼叫端也能知道建立的是什麼。
2. 不必每次都建立新物件
每次呼叫 init 都一定會建立新實例。
相較之下,工廠方法可以回傳快取物件或既有的單例。像 Bool 的真假值這類固定值,重用會有效率得多。
3. 回傳型別更有彈性
我個人覺得這是最強大的優點。
工廠方法可以回傳宣告型別的子型別或協定實作。呼叫端不必知道具體型別。
protocol Shape { func area() -> Double }
enum ShapeFactory {
// 依條件回傳不同實作
static func make(sides: Int) -> Shape {
sides == 4 ? Square() : Triangle()
}
}
make(sides:) 的回傳型別只有一個 Shape,但實際會產生符合情境的實作。
4. 可以更平順地處理失敗
雖然可以使用 init? 或 throws,但工廠方法很適合直接回傳選用型別,或用 Result 包裝結果。建立邏輯複雜時,流程會更清楚。
和 init 有什麼不同?一眼比較
怕容易混淆,所以整理成表格。
| 分類 | init(建構子) | static func make(靜態工廠) |
|---|---|---|
| 名稱 | 固定為 init | 可自由命名 |
| 新實例 | 每次強制建立 | 可重用、可快取 |
| 回傳型別 | 只能是自身型別 | 可為子型別或協定 |
| 失敗處理 | init? / throws | 選用型別、Result 等皆可 |
| 缺點 | 無法用名稱區分 | 限制子類別化,探索性較低 |
缺點也坦白說明一下。
如果只有工廠方法而沒有 public init,這個型別就很難透過繼承擴充。
建構子會直接出現在自動完成中,但要找 make 通常得先知道名稱,因此探索性稍低。建議使用 make、create、from 這類慣例名稱。
那麼什麼時候該用?
我在實務上的判斷方式如下。
- 只需填入儲存屬性 → 直接用
init - 建立方式有多種,想用名稱區分 →
make - 需依條件回傳不同型別 →
make - 想重用或快取物件 →
make
總結來說,當建立不只是「填入值」,而是成為一個決策流程時,工廠方法就能發揮價值。
反過來,所有建立都包成 make,程式碼反而會變冗長。重點是只在需要時使用。
一開始可能不習慣,但掌握意圖後,讀程式碼會變得更有趣。下次遇到類似情況,不妨試試 make,相信會有幫助 🙂

