現在專案已經有一個雛形了,是時候可以導入依賴注入 ( Dependenct Injection,簡稱 DI )了,這次我選用的是 Dagger 2。
在了解什麼是依賴注入前,我們需要先了解一些觀念。
先來一個小程式:
Tom 是個上班族(
社畜),他平時上班時會騎機車出門。
class Motor {
fun drive() {......}
}
class Tom {
val motor = Motor()
fun goOutside() {
motor.drive()
}
}
如果今天 Tom 要出門時,剛好下雨了,於是他只好開汽車出門:
class Car {
fun drive() {......}
}
class Tom {
// val motor = Motor()
val car = Car()
fun goOutside() {
// motor.drive()
car.drive()
}
}
聰明的 Tom 透過開關註解來切換所使用的交通工具 --- 好吧,起碼程式碼可以動...
可以發現只不過是換種代步工具,結果我們幾乎把程式碼重寫了一遍,如果我們還和 Tom 一樣聰明,切換交通工具時就會更頻繁的改動他,那麼我們能夠怎麼處理呢?
interface Driver {
fun drive()
}
class Motor : Driver {
override fun drive() {......}
}
class Car : Driver {
override fun drive() {......}
}
class Tom(private val driver: Driver) {
fun goOutside() {
driver.drive()
}
}
......
fun main() {
val motor = Motor()
val car = Car()
val tom = if (今天下雨) {
// 今天開汽車
Tom(car)
} else {
// 今天騎機車
Tom(motor)
}
tom.goOutside()
}
在這裏我們除了定義一個 interface 來描述並統一行為外,我們讓 汽機車先在外面實現然後傳給 Tom
,透過在外面換車來避免影響 Tom
內的程式碼。
我們讓
Tom
的依賴 (Motor、Car) 不在Tom
內實現,而是 交由外部控制 ,這就是 控制反轉 (IoC)
實現 IoC 的好處很多,諸如:
可測試性:如果依賴在內部實作,測試時很難為內部的依賴 Mock ,常見於在 Class 裡實現了某個 Data class ,結果要測試時發現沒辦法塞假資料...。
Tom 的家人 (其他的 Class) 也可以使用在外部的依賴,共用同一個實體。
IoC 在現今許多框架都有實現,而實現 IoC 的其中一種方式就是 DI 。
我們稍微改變一下狀況:
某天 Tom 自己的車子壞了,但是他又急著要去上班,於是拜託他朋友順路載他一程。
短時間內還可以,但是如果哪天他朋友沒有空就麻煩了。
由上可知 Tom 委託了他的朋友幫忙載他一程,我們可以說 Tom 依賴他朋友,a.k.a. 司機工具人。
既然如此 Tom 就可以改叫 Uber ,Uber 有個好處, 如果 A 司機沒空 Uber 會自動調派 B 司機去載他。
Tom 只要跟 Uber 打過招呼,時間一到 Uber 就會派一個司機去載 Tom 。
我們可以這麼說: Tom 委託 (依賴) 的對象從 司機 變成了 Uber 公司 。
現在重新整理一下關係,此時:
寫成程式碼大概是這樣:
interface Driver {
fun pickupPassenger()
}
class UberDriver : Driver {
override fun pickupPassenger() {......}
}
class Tom(private val driver: Driver) {
fun goToWork() {
driver.pickupPassenger()
}
}
object UberInjector {
fun provideDriver() = UberDriver()
}
......
fun main() {
// Tom 需要司機, uber 自動指派一名 Driver 去載他
// => UberInjector 注入了一個 Driver 給 Tom
val tom = Tom(UberInjector.provideDriver())
tom.goToWork()
}
到這邊我們可以做一個結論:
實現 控制反轉 的其中一種方式叫做 依賴注入 ,而 Dagger 2 是幫助實現依賴注入的一個 library 。
現在可以選擇的依賴注入的框架有很多,諸如 koin、kodein 等等,具體使用那個就看各自需求。考慮效能問題可以選擇 Dagger 2 ;在乎 framework 大小、容易上手或是 kotlin 的愛用者就可以選擇 koin 或 kodein 。
今天花了一個篇幅釐清了 DI 的原理,明天再開始導入 Dagger 2 。