標籤:
安卓架構...有什麼清晰的方式?
前言
我們知道寫出有品質的軟體是複雜而且困難的:它不僅僅在於滿足所有的需求,同時也應該是健壯的、易於維護的、方便測試的、非常靈活的(能夠靈活的改變內容,如模組加減)。清晰的架構(The Clean Architecture)就是在這種需求下誕生,而且能夠成為在軟體開發過程中的一個好的選擇。
清爽的架構的想法非常簡單:它代表一組方式規則,能夠產生如下的系統:
- 與架構無關
- 易於測試
- 與UI無關
- 與資料庫無關
- 與其他外部組件無關
在實際應用過程中,沒有必要像圖中那樣來進行劃分,因為只是概要圖。但是應該做到單向依賴:代碼的依賴性只能夠向內, 所有內部的模組不應該知道任何外部模組的資訊,更不要說有任何調用。
下面是一些相關詞彙,用來協助理解:
- 執行個體(Entities): 這些是軟體實現過程中的業務執行個體(Object)。
- 資料互動器(Use Cases,又稱interactor): 資料互動器負責協調執行個體的資料轉送。
- 介面適配器(Interface Adapters): 介面適配器負責資料轉換,將從執行個體和資料互動器中傳遞出來的資料進行解析。Presenter和Controller就屬於這裡。 (從原文中可以理解,在設計usecase 和entities的時候,盡量讓這兩層的資料變得簡單、便於使用,提升這兩層的效率。這樣,將更深層中的工作量變得更少。只是個人理解。)
- 架構和驅動(Frameworks and Drivers): 這一部分負責所有相關細節,如: UI, 工具,架構等(這裡暫時無法理解)。
需要更加清晰的理解,可以翻牆看下連結中的視頻。
安卓架構
架構的目的是分離原則:內部的商務邏輯完全與外部無關,因此,在測試中就不需要載入外部依賴。
為了實現這個目標,我建議將程式分成三層,其中每一層有自己的工作,並且在運行過程中與其它層隔離開來。
值得一提的是,通過為每一層建立自己的資料模型,能夠實現各層獨立。(在代碼中你會發現,需要用data mapper來實現資料轉換,作為代價,你的model將橫跨整個APP。)
上面內容的圖解:
註: 我沒有用任何外部庫(除了Gson來解析json資料,和junit,mockito,robolectric,espresso用來測試)。這樣,我就能把例子弄得更加清楚。但是,千萬不要自己重新去寫一個輪子,能從別人那兒拿來用的就從別人那拿(你要好好挑一挑)。
展示層(Presentation Layer)
與頁面邏輯和頁面動畫相關的邏輯在這一層中。它主要用的是MVP架構。你也可以按照需要使用MVC,MVVM等。值得強調的是,在展示層,fragment和activity僅僅是View,其中沒有任何除了UI邏輯以外的邏輯,同時渲染也在這一層被實現。
Presenter在這一層中與資料互動器(interactor,use case)一起以新線程(UI線程外)的方式工作,然後通過回呼函數將需要展示的資料交給View。
邏輯層(Domain layer)
本層商務規則為: 所有的邏輯在這一層發生。在項目層面,你會發現所有的資料互動器在這一層被實現。
這一層為純java模組,不包括任何對android的依賴。所有的層外組件通過介面與邏輯層中的執行個體(Object)互動。
資料層(Data Layer)
資料層就像一個倉庫一樣,所有APP需要的資料都通過位於邏輯層中的倉庫介面(Repository interface)被取出。這個介面使用了倉庫模式,也就是,通過一個工廠,按需挑選出各種資料向上傳遞。打個比方,當通過id擷取使用者的時候,程式會判斷緩衝中是否有使用者資料,如果沒有的話,從伺服器中擷取,並存在本地,如果有的話,直接從緩衝中擷取。
用這種方式的原因是:使用者不在乎資料的來源是什麼,他們只在乎資料是否被擷取到。
注意:代碼中有這一介面的簡單例子。還是那句話,不要重新做輪子。
錯誤處理
我的策略是使用回呼函數,因此,比如在資料倉儲中有情況發生,將產生兩個回呼函數,onResponse()和onError()。後者將把異常打包進入一個叫做“ErrorBundle”的封裝類: 這個方式帶來了一定的問題,因為這樣產生了一連串的,一層層上報的回呼函數,直通展示層,被展示層處理掉。這種方式在一定程度上降低了代碼可讀性。
另一方法是,我可以實現EventBus,每次有錯誤發生,EventBus將會將時間分配給指定處理函數。但是這樣需要非常嚴謹,小心的處理,否則會不好管理。
測試
關於測試,我給出一點建議。
- 展示層:使用安卓自動化測試載入器instrumentation和espresso來實現整合和功能性測試。
- 邏輯層:使用Junit 和 mockito來進行單元測試。
- 資料層:Robolectric(因為本層有安卓依賴)和junit以及mockito來進行測試。
結論
架構的設計是以實際情況為走向,而不是架構。要根據實際情況來進行架構設計。在設計架構的時候要確保:
代碼例子: 參見github
安卓架構...有什麼清晰的方式?