- 原文地址:Dependency injection in a multi module project
- 原文做者:Ben Weiss
- 譯文出自:掘金翻譯計劃
- 本文永久連接:github.com/xitu/gold-m…
- 譯者:Mirosalva
- 校對者:JasonZ,wenny
總的來講,這不是一篇關於依賴注入的文章,也不是關於咱們爲何選擇庫 X 而不是庫 Y 的文章。 相反的,本文從依賴注入的角度介紹了咱們對 Plaid 進行模塊化實踐的主要成果。前端
在前面的文章中,我寫過 Plaid 應用模塊化的總體過程。 一款拼接應用 Plaid — 總體到模塊化: 模塊化 Plaid 應用的初衷、過程和結果java
讓我以鳥瞰圖的形式快速回顧一下 Plaid 的樣子。android
咱們有一個包含主啓動 activity 的 app
模塊,同時也有一些依賴 app
模塊的動態功能模塊(DFM)。每個 DFM 都包含至少一個與所討論功能相關的 activity、代碼和資源。ios
app
模塊依賴一個包含了共享的代碼和資源以及第三方庫的 core
模塊。git
在咱們開始模塊化操做和以 Dagger 爲主介紹依賴注入以前,先來熟悉下 Plaid 的相關類和函數:github
class DesignerNewsInjector {
fun providesApi(...): DesignerNewsService { ... }
}
複製代碼
雖然這是一個很是好的解決方案,但咱們仍是手工編寫了大量的樣板代碼。後端
在任何須要注入的地方,咱們都須要在合適的時機調用底層函數,大多數狀況下不是在對象初始化時就是在 onCreate 方法中。bash
依賴注入基本上意味着你不用在你須要的地方建立它們,而是在別的地方建立。而後這些對象的引用能夠被傳遞到須要使用它們的類中。markdown
這點能夠經過本身編寫或者集成某個依賴注入庫來實現,咱們選擇了集成 Dagger 2。多虧了 Dagger,爲了獲取一個可使用的已初始化的 service,咱們全部要作的就是以下內容:app
@Inject lateinit var service: DesignerNewsService
複製代碼
全部對 service 的依賴能夠變成 provides 函數的傳參。咱們爲依賴注入需求選擇了 Dagger 意味着咱們的依賴圖在編譯階段會被建立。下面的章節中要記住這一點。
當咱們決定引入 Dagger 到 Plaid 應用時,咱們已經學到了寶貴的一課,尤爲是對模塊化。
不要試圖一次就覆蓋太多內容。
這意味着花一些時間研究清楚實現一個新功能的最小必要範圍是有意義的。咱們接下來要討論的 MVP,即在團隊內部審視咱們是否在向着正確的方向前進。堅持這種作法能夠防止咱們進行太大而沒法高效利用的變動。這也容許咱們在整個代碼庫中逐步推出更改,與此同時每一個人的任務也可持續進行。
在 Plaid 應用內咱們使用已驗證後的 about
功能模塊做爲 Dagger 的練習模塊。這裏咱們能夠添加 Dagger 而不會干擾到其餘模塊或負載。你能夠在這裏查看初始提交。
當爲一個單塊應用引入依賴注入庫時,一般整個應用有個單一的依賴圖。
這可使組件間共享依賴。在一些庫中,依賴能夠被設置做用域來避免衝突,或者爲被注入對象提供一種特殊的實現。
對一個模塊化的應用,尤爲是使用動態功能模塊的應用這卻不起做用。讓咱們仔細地研究下應用和動態功能模塊如何彼此依賴。一個動態功能模塊知道 application 模塊的存在。application 模塊大體知道動態功能模塊的存在,可是不能直接執行該模塊的代碼。對於依賴注入,這意味着總體圖必須被分解成片。
對一個模塊化應用,簡單的依賴圖一般大體長成下面這樣。
更具體的是,Plaid 中組件規劃圖看起來像這樣。
每一個 DFM 都有它本身的組件,以組件所在的功能模塊命名。app
模塊中的 HomeComponent
組件就是如此。
還有一個包含共享依賴項的組件,它位於 core
庫中並被稱做 CoreComponent
。CoreComponent
背後的主要思想是提供可被整個應用使用的對象。它結合了一些 Dagger 模塊,這些模塊位於 core
庫並能夠在整個應用中複用。
此外,因爲依賴圖具備方向性,所以只能經過如下方式共享 Dagger 組件: DFM 圖能夠從 application 模塊來訪問 Dagger 組件。application 模塊能夠從它依賴的庫中訪問組件,但方向反過來則不行。
爲了共享 Dagger 組件,它們須要被整個應用訪問到。在 Plaid 中咱們決定使用 Application 類來讓咱們的 CoreComponent
變得可訪問。
class PlaidApplication : Application() {
private val coreComponent: CoreComponent by lazy {
DaggerCoreComponent
.builder()
.markdownModule(MarkdownModule(resources.displayMetrics))
.build()
}
companion object {
@JvmStatic fun coreComponent(context: Context) =
(context.applicationContext as PlaidApplication).coreComponent
}
}
複製代碼
被實例化的 CoreComponent 組件如今能夠從應用中任何具備 context 的地方來訪問,經過調用 PlaidApplication.coreComponent(context) 的方式。
使用一個擴展函數可使 this 更好地訪問:
fun Activity.coreComponent() = PlaidApplication.coreComponent(this)
複製代碼
爲了把 CoreComponent
包含到另外一個組件中,有必要在組件建立時提供它。讓咱們看一下在 SearchComponent` 中是如何作到的:
@Component(modules = [...], dependencies = [CoreComponent::class])
interface SearchComponent {
@Component.Builder
interface Builder {
fun coreComponent(coreComponent: CoreComponent): Builder
// modules
}
}
複製代碼
在生成的 DaggerSearchComponent
作初始化時咱們像這樣設置了 CoreComponent
:
DaggerSearchComponent.builder()
.coreComponent(activity.coreComponent())
// modules
.build()
.inject(activity)
複製代碼
這裏的技巧是把 CoreComponent
設置爲 SearchComponent
的一個依賴:
@Component(
modules = [SearchModule::class],
dependencies = [CoreComponent::class]
)
interface SearchComponent : BaseActivityComponent<SearchActivity>
複製代碼
CoreComponent
是 SearchComponent
的一個依賴。當 CoreComponent
像上面那樣被引用爲 SearchComponent
的一個組件依賴時,全部的 CoreComponent
方法能夠在 SearchComponent
中使用,或者在其餘 Dagger 組件中使用,就好像他們變成註解 @Provides
標記的方法。
這樣作的的一個好處是:在功能圖中無需重複 @Modules
,卻能夠經過 CoreComponent
或其餘與之綁定的模塊來透明地提供出去。
例如,CoreDataModule
綁定在 CoreComponent
中,並提供 Retrofit
等。Retrofit
實例如今能夠被任何與 CoreComponent
合併的組件訪問到。
讀完這篇文章,你能夠看到模塊化你的應用須要把依賴注入考慮進去。引入的功能模塊邊界經過分離的依賴圖反映在依賴注入中。意識到這個限制可有助於爲共享組件找到合適的位置。
你能夠深刻到代碼中來查看咱們如何使用 Dagger 解決 Plaid 中的依賴注入問題。
CoreComponent
是一個好的閱讀開端,AboutComponent
也是,由於它沒有太多的外部依賴。
若是發現譯文存在錯誤或其餘須要改進的地方,歡迎到 掘金翻譯計劃 對譯文進行修改並 PR,也可得到相應獎勵積分。文章開頭的 本文永久連接 即爲本文在 GitHub 上的 MarkDown 連接。
掘金翻譯計劃 是一個翻譯優質互聯網技術文章的社區,文章來源爲 掘金 上的英文分享文章。內容覆蓋 Android、iOS、前端、後端、區塊鏈、產品、設計、人工智能等領域,想要查看更多優質譯文請持續關注 掘金翻譯計劃、官方微博、知乎專欄。