Android 依賴注入 (Dependency Injection)

依賴注入 (Dependency Injection, DI) 這個名詞聽起來很嚇人,但其實概念非常簡單。

什麼是依賴 (Dependency)?

如果 Car 類別需要 Engine 類別才能運作,我們就說 Car 依賴Engine

沒用 DI 的寫法

Car 自己負責製造 Engine

class Engine {
    fun start() { ... }
}

class Car {
    // Car 自己在這個類別內部 "new" 了一個 Engine
    private val engine = Engine() 

    fun drive() {
        engine.start()
    }
}

fun main() {
    val car = Car() // 我們只需要建立 Car,它自己會搞定 Engine
    car.drive()
}

缺點

  1. 耦合度高 (Tight Coupling)CarEngine 綁死了。如果我想換成 ElectricEngine,我必須修改 Car 的程式碼。
  2. 難以測試:做單元測試時,我無法把 Engine 換成假的 MockEngine,因此無法單獨測試 Car 的邏輯。

使用 DI 的寫法

Car 不自己製造 Engine,而是由外面的人 (Injector) 把它注入進來。

class Car(private val engine: Engine) {
    fun drive() {
        engine.start()
    }
}

fun main() {
    // 手動注入 (Manual Injection)
    val engine = Engine()
    val car = Car(engine) // 從外面傳進去
    car.drive()
}

優點

  1. 解耦Car 不知道 Engine 是怎麼來的,只要是 Engine 都能用。
  2. 可測試
    val mockEngine = TestEngine() // 假的引擎
    val car = Car(mockEngine)
    // 現在我們可以驗證 car.drive() 是否正確呼叫了 engine.start()
    

為何需要 DI 框架 (如 Hilt/Dagger)?

手動 DI 在小專案還可以,但在大專案中: Car 依賴 Engine, Engine 依賴 Piston, Piston 依賴 Metal...

如果你要建立一個 Car,你必須手動寫這一大串:

val metal = Metal()
val piston = Piston(metal)
val engine = Engine(piston)
val car = Car(engine)

這叫做 Boilerplate Code (樣板程式碼)

DI 框架 (如 Hilt) 的作用就是自動幫你建立這些物件關係圖 (Object Graph)。你只需要加幾個註解 (@Inject),框架就會自動幫你 "new" 這些物件並塞進去。

小結

DI 的核心精神就是:物件所需的資源,通通由外部提供,不自己建立。這讓程式碼更靈活、更模組化。在下一章,我們將介紹 Android 官方推薦的 DI 框架:Hilt