[譯] Android 架構:Part 3 —— 應用整潔架構

到目前爲止,在這個系列中,咱們已經講解了一些 初學者易犯的錯誤,以介紹了整潔架構。在這最後一部分,咱們會介紹最後一個難題:標籤,或者準確地說,組件。java

譯者注:看完了這一部分,還有第四部分。在第四部分將會提供一個很酷的示範項目。android

首先,我會移除在 Android 項目中不使用的東西,添加一些在 Uncle Bob 的圖中找不到的但咱們須要使用的東西,看起來像這樣:數據庫

我會從中心(抽象)講到邊緣(具體)。緩存

Entities

實體(又稱領域對象或業務對象)是 app 的核心。它們表明 app 的主要功能,你應該可以僅經過查看實體來講出 app 是作什麼的。它們包含業務邏輯 —— 僅限於驗證和相似的東西。它們不和真實的外部世界交互,也不會處理持久化。若是你有一個新聞 app,那麼實體就是類別、文章和商業。網絡

Use Cases

用例,也稱爲 interactor(交互器),也能夠叫作 business service (業務服務對象),是實體的擴展,是 business logic(業務邏輯)的擴展。也就是說,它們包含的業務邏輯不限於一個實體,而是能夠處理不少實體。一個好用例的標準是,你可使用一句簡單的經常使用語言來描述它是作什麼的,例如,「把錢從一個帳戶轉移到另外一個帳戶」。你甚至可使用這樣的命名系統來命名該類,例如 TransferMoneyUseCase。架構

Repositories

倉庫用於持久化實體。就這麼簡單。它們定義爲接口,而且用在要對實體執行增刪查改操做的用例的輸出端口。此外,它們能夠公開一些與持久化相關的複雜操做,譬如過濾、聚合等。具體持久化策略,譬如數據庫或網絡,在外層中實現。例如,你能夠把接口命名爲 AccountRepository。app

Presenters

若是你熟悉 MVP 模式,Presenter 作你想讓它作的事情。它們處理用戶交互,調用恰當的業務邏輯,並將數據發送給 UI 渲染。這裏一般有各類類型的模型之間的映射轉換。有些人會在這裏使用控制器,這是能夠的。咱們使用的 Presenter 被正式稱爲監督控制器,咱們一般根據屏幕方向爲每一個界面定義一個或兩個 Presenter,而且 Presenter 的生命週期和相關的 View 的生命週期綁定。一個建議:嘗試以技術無關的方式命名 Presenter 中的方法,僞裝你不知道 View 是用什麼技術實現的。因此,若是在 View 中有方法名爲 onSubmitOrderButtonClicked 和 onUserListItemSelected,那麼處理這些事件的相應的 Presenter 中的方法能夠被命名爲 submitOrder 和 selectUser。post

Device

這個組件在先前通知那個例子中已經被玩壞了。它包含了諸如傳感器、鬧鐘、通知、播放器、各類 *Manager 等等真實 Android 功能的實現。它包含兩部分組件。第一部分是定義在內層的接口,業務邏輯用它來做爲和外部世界通訊的輸出端口。第二部分,也畫在圖中,是那些接口的實現。所以,好比,你能夠定義名爲 Gyroscope, Alarm, Notifications, 和 Player 的接口。請注意,這些名稱是抽象的技術無關的。業務邏輯不關心通知如何顯示,播放器如何播放聲音,或螺旋儀的數據來自哪裏。你能夠建立一個將通知寫入終端,將聲音數據寫到日誌,或者從預先定義好的文件中收集螺旋儀數據的實現。這樣的實現對於調試或建立一個用於你編碼的肯定性的環境是頗有用的。固然,你必須建立諸如 AndroidAlarm,NativePlayer 等等的實現。在大多數狀況下,這些實現僅僅是 Android Manager 類的包裝。測試

DB & API

這裏沒有哲學。將倉庫的實現放在此組件中。全部的底層持久化的東西應該放在這裏:DAO,ORM,Retrofit(或別的),JSON 解析等等。你還能夠在這裏實現緩存策略或者簡單地在內存中(in-memory)持久化,直到你完成了 app 的其他部分。咱們團隊最近進行了一個有趣的討論。問題是這樣:倉庫是否應該公開諸如 fetchUsersOffline(fetchUsersFromCache)和 fetchUsersOnline(fetchUsersFromInternet)之類的方法?換句話說,業務邏輯是否應該知道數據來自哪裏。讀完這篇文章的全部內容後,答案很簡單:不。但這裏有個陷阱。若是關於數據源的決策是業務邏輯的一部分 —— 譬如,用戶能夠選擇或者 app 有一個明確的離線模式 —— 而後你能夠添加這樣的區分。但我不會爲每一個請求定義兩種方法。我可能會在倉庫中公開 enterOfflineMode 和 exitOfflineMode 這樣的方法。或者若是它適用於全部倉庫,咱們可使用 enter 和 exit 方法定義一個 OfflineMode 接口,並在業務端使用它,讓倉庫去查詢它的模式而且在內部決策。fetch

UI

這裏的哲學更少。將和 Android UI 相關的東西放在這裏。Activity、Fragment、View、Adapter 等等。完了。

模塊(Modules)

下圖顯示了咱們如何將全部這些組件分解成 Android Studio 模塊。 你可能會發現另外一種更合適的分法。

咱們將實體、用例、倉庫和設備接口分到領域模塊。若是你想要一個額外的挑戰(獎勵是永恆的榮耀和徹底整潔的設計),你可使該模塊成爲一個純 java 模塊。這將阻止你走捷徑將一些 Android 相關的東西放在這裏。

設備模塊包含全部和 Android 相關的東西(除了數據持久化和 UI)。數據模塊應該持有和數據持久化相關的東西,正如咱們說過的那樣。你不能把這二者弄成 java 模塊,由於它們須要訪問各類 Andriod 相關的東西。你能夠把它們弄成 Android library。

最後,咱們將和 UI (包括 Presenter)相關的全部東西分到 UI 模塊。你能夠明確地將其命名爲 UI,可是因爲全部 Android 的東西都在這裏,咱們保留它 「app」 的名字,正如 Android Studio 在建立項目時所命名的那樣。

好點了嗎?

爲了回答這個問題,我丟開 Uncle Bob 的圖,將先前描述的組件攤開到圖中,就像以前那些咱們曾經評估過的架構類型那樣。這樣作以後,咱們獲得:

如今讓咱們來使用與以前的架構相同的評價標準。

它徹底分離到模塊級別、包級別、類級別,因此應該知足單一職責原則。

咱們已經將 Android 和真實世界的東西儘量地推到邊緣,業務邏輯再也沒有直接接觸 Android。

咱們很好地分離類以方便測試。接觸 Android 世界的類可使用 Android 測試例進行測試,沒有接觸的類可使用 JUnit 進行測試。可能有人惡意稱它爲類爆炸,我稱之爲可測試。:)

這可能很複雜——但值得

我但願我精心挑選的標準,不只能迎合 Clean Architecture,也能說服你一試。看起來很複雜,並且有不少細節,但這是值得的。一旦你把全部都串起來,測試就會變的更容易,BUG 更容易定位,新功能更容易添加,代碼更易讀和維護,一切均可以完美運行,宇宙都被知足了。

因此,就是這樣。若是你尚未這樣作,請看本系列先前的文章:初學者易犯的錯誤介紹 Clean Architecture。若是你有任何意見或問題,請留言。咱們老是有興趣聽到你的想法。

閱讀 Andriod 架構系列 第四部分

原文

相關文章
相關標籤/搜索