軟體設計

建構式注入 vs 屬性注入 vs 方法注入,該用哪一種?(Swift DI 總整理)

使用 Swift 開發 iOS App 時,一定會遇到一個令人煩惱的問題。

閱讀 3 分鐘
建構式注入 vs 屬性注入 vs 方法注入,該用哪一種?(Swift DI 總整理) 封面圖

使用 Swift 開發 iOS App 時,一定會遇到一個令人煩惱的問題。

「相依性注入到底該採用哪種方式才對?」

先說結論。

除非有特殊理由,否則使用 建構式(init)注入,是最接近正解的選擇。

接下來會以實際經驗說明原因,以及其餘兩種方式適合在什麼時候使用。


三種注入方式,先掌握重點

在感到混亂之前,先把架構整理好。Swift 中注入相依性的方式大致有三種。

  1. 建構式(init)注入init透過參數接收相依性
  2. 屬性注入var稍後再指定給屬性
  3. 方法注入 — 透過獨立的方法注入

先看看最推薦的建構式注入程式碼。

final class OrderService {
    private let repo: MemberRepository // let 確保不可變性

    init(repo: MemberRepository) { // 建構式(init) 注入
        self.repo = repo
    }
}

不需要額外函式庫,只使用純 Swift 語法,因此程式碼很簡潔。

相較之下,屬性注入可以寫得這麼短。

final class OrderService {
    var repo: MemberRepository! // 只有一行,看起來很方便,但…
}

一眼看去,屬性注入確實比較簡單。不過這份便利之後可能會造成問題。


為什麼不建議使用屬性注入?

先談談我親身遇到的情況。

屬性注入在測試時非常不方便。建立物件後若忘了注入,就會發生選用型別強制解包崩潰;光看型別也無法知道要填入什麼才算完成。

如果使用建構式注入,就能像 OrderService(repo: mockRepo) 一樣,直接用純 Swift 程式碼傳入 Mock 物件。

第二個問題是無法使用 let 關鍵字。

建構式注入可以將屬性宣告為 let,注入一次後就會成為不可變物件。

相反地,屬性注入是 var,值可以隨時變更,因此容易產生錯誤。

第三個問題是循環參照。

如果 A 參照 B,而 B 又參照 A,屬性注入要等 App 執行後才可能發現,必須進入該畫面才會崩潰。

建構式注入會在建立物件時立即暴露問題;若設計混亂,甚至會無法編譯,因此能更早修正問題。

循環參照,連崩潰的時機都不同
循環參照,連崩潰的時機都不同
只使用欄位注入的日子裡,每次撰寫測試都讓我很困擾
只使用欄位注入的日子裡,每次撰寫測試都讓我很困擾

一眼看懂的比較表

用文字說明容易混淆,所以整理成表格。(截至 2026 年 Swift 社群建議的方向)

分類 建構式(init)注入 屬性注入 方法注入
不可變性(let) 可以 ⭕ 不可以 ❌ 不可以 ❌
測試便利性 普通
循環參照偵測 建立時立即偵測 較晚發現 較晚發現
必要/選用相依性 適合必要相依性 難以區分 適合選用相依性
程式碼簡潔度 普通 非常簡潔 普通

只看表格也能看出整體方向了吧?

建構式注入在大多數項目中都勝出。因此,Swinject 或 Factory 等 DI 函式庫的文件也都將建構式注入列為預設建議。


那麼其餘兩種方式該在什麼時候使用?

這不是說任何情況都只能使用建構式注入。每種方式都有適合自己的場景。

方法注入很適合選用相依性。

當注入對象可以不存在,也就是有就使用、沒有也沒關係的相依性,可以透過 configure(with:) 這類方法彈性地注入。

屬性注入若要使用,適合用在無法自行控制初始化時機的地方,例如透過 Storyboard 建立的視圖控制器。

在一般應用程式碼中,最好盡量避免使用。

總結如下。

  • 必要相依性 → 建構式注入
  • 選用相依性 → 方法注入
  • 一般程式碼中的屬性注入 → 盡量避免

常見問題

Q. 使用 Factory 這類 DI 函式庫後,建構式注入會更方便嗎?

會。在 Swinject 或 Factory 中註冊相依性的建立方式後,容器會代替你建立要傳給建構式的物件。程式碼變短,同時仍能保有 let不可變性等優點。

Q. 如果建構式的參數變得太多呢?

這不是注入方式的問題,而是該類別負責太多工作的訊號。請先考慮拆分責任並分離類別。

Q. 小型專案也一定需要 DI 函式庫嗎?

不需要。如果只有幾個畫面,只要使用 init 直接傳入相依性的純建構式注入就足夠了。

感到猶豫時,依這個順序選擇就簡單了
感到猶豫時,依這個順序選擇就簡單了

一開始很容易被屬性注入的便利性吸引,但實際撰寫測試並與他人協作後,就會親身感受到為什麼大家如此強調建構式注入的優點。

如果不知道該怎麼選,先從建構式注入開始吧。程式碼越來越大時,你會慶幸做了這個選擇。