Tina Tang's Blog

在哪裡跌倒了,就在哪裡躺下來

0%

Android筆記(46)-Android架構

比較 Android 架構(MVC、MVP、MVVM、Clean Architecture、MVI)


MVC(Model-View-Controller)

MVC 是最古老和最廣為人知的架構模式之一。

  • 在啟動小型專案或快速原型設計時使用 MVC。
  • 它在 data(Model)UI(View)邏輯(Controller)之間提供了明確的分離。
  • 但是 MVC 會導致元件之間的高耦合單元測試的困難。

MVC 將程式碼劃分為三個部分:

說明
View Layout XML 文件。
Model 負責管理商業資料邏輯,如網絡請求、資料庫處理。
Controller Activity 負責處理表現邏輯。

MVC 希望 Activity / Fragment 是只處理表現邏輯的部分 ,但現實是 Activity 不可避免要處理 UI,也要處理和使用者的互動,說明 Activity 也承擔了 View 的角色。那麼這個架構就會造成 Activity 中融合了 view 和 logic 的代碼,分離程度不夠。


MVP(Model-View-Presenter)

MVP 是 MVC 的演變,它進一步分離了 UI 和商業邏輯(business logic)的關注點。

  • 將 MVP 用於要求更複雜的中型專案。
  • 它強制將 ModelViewPresenter 明確分開,使程式碼更易於理解和測試。
  • MVP 允許更好的可測試性,並促進單一責任原則(Single Responsibility Principle)
  • 但是,它仍然可能受到過多的樣板程式碼的影響,並且隨著專案的擴展而變得複雜。

為了將 Activity 中的表現邏輯徹底分離出來,業界提出了 MVP 的設計。

MVP 將程式碼劃分為三個部分:

說明
View Activity 和 Layout XML 文件。
Model 負責管理商業資料邏輯,如網絡請求、資料庫處理。
Presenter 負責處理表現邏輯。

在實現細節上,View 和 Presenter 中間會定義一個協議介面 Contract,這個介面會約定 View 如何向 Presenter 發指令和 Presenter 如何 Callback 給 View。這樣的架構里 Activity 不再有表現邏輯的部分,Activity 作為 View 的角色只處理和 UI 有關的事情。但還是存在一些缺點:

  • 雙向依賴: View 和 Presenter 是雙向依賴的,一旦 View 層做出改變,相應地 Presenter 也需要做出調整。在業務語境下,View 層變化是大概率事件;
  • 內存泄漏風險: Presenter 持有 View 層的引用,當用戶關閉了 View 層,但 Model 層仍然在進行耗時操作,就會有內存泄漏風險。雖然有解決辦法,但還是存在風險點和複雜度(弱引用 / onDestroy() 回收 Presenter)。
  • 協議介面類膨脹: View 層和 Presenter 層的交互需要定義介面方法,當交互非常複雜時,需要定義很多介面方法和 callback 方法,也不好維護。

MVVM(Model-View-ViewModel)

MVVM 是一種利用數據綁定(data binding)和響應式(reactive)程式設計的現代體系結構模式。

  • 將 MVVM 用於大型專案,重點關注數據驅動的 UI 和複雜的數據流。
  • 它在 ViewViewModelModel 之間提供了明確的分離。
  • MVVM 利用雙向數據綁定(data binding),使 UI 更新自動並減少樣板程式碼。
  • 這種架構允許更好的解耦、可測試性和可維護性。
  • 但是,採用 MVVM 需要對響應式程式設計和 data binding 概念有很好的理解。

MVVM 將程式碼劃分為三個部分:

說明
View Activity 和 Layout XML 文件,與 MVP 中 View 的概念相同。
Model 負責管理業務數據邏輯,如網絡請求、資料庫處理,與 MVP 中 Model 的概念相同。
ViewModel 存儲視圖狀態,負責處理表現邏輯,並將數據設置給可觀察數據容器。

在實現細節上,View 和 Presenter 從雙向依賴變成 View 可以向 ViewModel 發指令,但 ViewModel 不會直接向 View 回調,而是讓 View 通過觀察者的模式去監聽數據的變化,有效規避了 MVP 雙向依賴的缺點。但 MVVM 本身也存在一些缺點:

  • 多數據流: View 與 ViewModel 的交互分散,缺少唯一修改源,不易於追蹤。
  • LiveData 膨脹: 複雜的頁面需要定義多個 MutableLiveData,並且都需要暴露為不可變的 LiveData。

DataBinding、ViewModel 和 LiveData 等組件是 Google 為了幫助我們實現 MVVM 模式提供的架構組件,它們並不是 MVVM 的本質,只是實現上的工具。

  • Lifecycle: 生命周期狀態回調。
  • LiveData: 可觀察的數據存儲類。
  • DataBinding: 可以自動同步 UI 和 data。
  • ViewModel: 存儲界面相關的數據,這些數據不會在手機旋轉等配置改變時丟失。

Clean Architecture

Clean Architecture 強調關注點和依賴關係規則的分離。

  • 將 Clean 架構用於大型複雜專案,重點關注可維護性和可測試性。
  • 它將代碼庫(codebase)劃分為多個層:PresentationDomainData,每個層都有自己的職責和依賴關係。
  • 簡潔的架構允許輕鬆更換元件並促進單元測試。
  • 引入了額外的複雜性,可能會針對較小的專案進行過度設計。

Clean Architecture(乾淨架構)是由 Robert C. Martin(又稱 Uncle Bob)提出的一種軟體設計理念。其主要目的是通過將系統的不同層次進行分離,使得程式碼更加靈活可測試易於維護。Clean Architecture 強調將業務邏輯與外部系統(例如框架、資料庫、UI 等)進行隔離,這樣可以減少依賴並提高系統的可擴展性和可測試性。

Clean Architecture 的核心概念

  1. 框架獨立性:核心業務邏輯不應該依賴於特定的框架或技術,框架只應作為工具,應該可以被替換而不影響核心邏輯。
  2. UI 獨立性:用戶界面不應依賴於核心邏輯,這意味著你可以更換 UI 技術(例如從 Web 界面換成移動應用),而不需要修改核心業務邏輯。
  3. 資料庫獨立性:應用程序不應依賴於特定的資料庫,核心邏輯不應該依賴於使用 SQL、NoSQL 或其他數據存儲。
  4. 可測試性:由於核心邏輯與外部系統隔離,因此更容易進行單元測試。業務邏輯可以在不依賴資料庫或 UI 的情況下進行測試。
  5. 關注點分離:系統的不同部分應該處理不同的問題。這是通過架構的層次結構來實現的。

Clean Architecture 的層次結構

  • Entities(實體層,核心業務邏輯):這是最內層,包含應用程序的業務規則和領域模型。實體是代表核心業務邏輯的對象,它們與外部系統無關。
  • Use Cases(用例層,應用邏輯):此層包含具體的應用邏輯,它使用業務邏輯來完成任務。它定義了應用的用例,並可以與實體進行交互來達成所需的結果。此層應該不依賴框架或基礎設施。
  • Interface Adapters(介面適配層):這一層充當應用核心與外部系統(如 UI、資料庫、Web 服務等)之間的橋樑。它將數據轉換成用例所需的格式,並將外部系統的數據轉換為內部邏輯可處理的格式。
  • Frameworks and Drivers(基礎設施層):最外層,包括框架、庫、資料庫、Web 伺服器和與外部系統的所有交互。這一層可以根據需要進行更換,對核心應用邏輯影響最小。
依賴規則

Clean Architecture 的關鍵在於「依賴規則」,即依賴關係應該始終指向內層。這意味著外層的代碼可以依賴於內層的代碼,但內層的代碼不應該依賴於外層的代碼。這樣可以確保核心業務邏輯與外部系統的解耦,增強系統的可維護性和靈活性。

Clean Architecture 的優點

  • 可維護性:由於層次分離,代碼更容易維護,當需要修改某一部分時,不會影響到其他部分。
  • 可測試性:核心業務邏輯不依賴外部系統,可以輕鬆地進行單元測試。
  • 靈活性:可以根據需求更換或替換不同的框架、資料庫或 UI,對核心業務邏輯的影響最小。
  • 可擴展性:架構支持系統的擴展,不同的系統模塊可以獨立發展。

實際應用範例

以一個簡單的電子商務應用為例:

  • Entities:產品(Product)、訂單(Order)、用戶(User)等核心業務對象。
  • Use Cases:加入購物車、處理支付、註冊用戶等應用邏輯。
  • Interface Adapters:Web 控制器、資料庫倉儲(Repositories)、用於展示的呈現者(Presenter)等。
  • Frameworks and Drivers:Web 框架(如 React 或 Django)、資料庫(如 PostgreSQL)、第三方支付處理器等。

通過遵循 Clean Architecture 的原則,當你更換 Web 框架或支付處理器時,無需修改核心業務邏輯,這樣可以大大提升系統的可維護性和靈活性。

從 MVVM 切入

在資料流向方面,Clean Architecture 相當於把 MVVM 中的 ViewModel 職責拆分成多個 UseCase,避免 ViewModel 太過膨脹(塞太多code)。

總結

Clean Architecture 的目的是將系統中的業務邏輯與外部技術(框架、資料庫、UI 等)進行有效分離,這樣可以確保系統的可測試性、可維護性、靈活性和可擴展性。這種架構設計使得每一層的責任更加明確,並能夠隨著需求的變化而輕鬆地調整系統的組成部分。


MVI(Model-View-Intent)

MVI 是一種新興的架構模式,旨在提供可預測(predictable)響應式(reactive) UI。

  • 將 MVI 用於需要嚴格的單向數據流(data flow)和高度互動式 UI 的專案。
  • 它將 ModelViewIntent 的關注點分開,提供清晰的數據流(data flow)方向。
  • MVI 促進了不變性(immutability)和回應式(reactive)程式設計,使得對狀態變化的推理變得更加容易。
  • 但是,MVI 可能會帶來額外的複雜性,尤其是對於簡單的專案。

MVI 模式的改動在於將 View 和 ViewModel 之間的多數據流改為基於 ViewState 的單數據流。

MVI 將程式碼劃分為四個部分:

說明
View Activity 和 Layout XML 文件,與 MVVM 中 View 的概念相同。
Intent 定義數據操作,是將數據傳到 Model 的唯一來源,相比 MVVM 是新的概念。
ViewModel 存儲視圖狀態,負責處理表現邏輯,並將 ViewState 設置給可觀察數據容器。
ViewState 一個數據類,包含頁面狀態和對應的數據。

在實現細節上,View 和 ViewModel 之間的多個交互(多 LiveData 數據流)變成了單數據流。無論 View 有多少個視圖狀態,只需要訂閱一個 ViewState 便可以獲取所有狀態,再根據 ViewState 去響應。當然,實踐中應該根據狀態之間的關聯程度來決定數據流的個數,不應該為了使用 MVI 模式而強行將多個無關的狀態壓縮在同一個數據流中。

  • 唯一可信源: 數據只有一個來源(ViewModel),與 MVVM 的思想相同;
  • 單數據流: View 和 ViewModel 之間只有一個數據流,只有一個地方可以修改數據,確保數據是安全穩定的。並且 View 只需要訂閱一個 ViewState 就可以獲取所有狀態和數據,相比 MVVM 是新的特性;
  • 響應式: ViewState 包含頁面當前的狀態和數據,View 通過訂閱 ViewState 就可以完成頁面刷新,相比於 MVVM 是新的特性。

但 MVI 本身也存在一些缺點:

  • State 膨脹: 所有視圖變化都轉換為 ViewState,還需要管理不同狀態下對應的數據。實踐中應該根據狀態之間的關聯程度來決定使用單流還是多流;
  • 內存開銷: ViewState 是不可變類,狀態變更時需要創建新的對象,存在一定內存開銷;
  • 局部刷新: View 根據 ViewState 響應,不易實現局部 Diff 刷新,可以使用 Flow#distinctUntilChanged() 來刷新來減少不必要的刷新。

Android 架構的比較

MVVM 和 MVP 的思想是相同的,最本質的概念就是 Activity 里做的事情太多了,所以要把 Activity 中與 UI 無關的部分抽離出來,交給別人做。這個 「別人」 在 MVP 里叫作 Presenter,在 MVVM 里叫作 ViewModel。而不論是 MVP 中的約定接口,還是 ViewModel 里的觀察者模式,這些都是實現上的細節而已。

MVI 與前者的主要區別不在於強調嚴格的單向數據流,而在於從命令式的開發模式,轉變為響應式的開發模式。

  • MVC 和 MVP 適用於較小的專案。
  • MVVM、Clean Architecture 和 MVI 為更大和更複雜的專案提供了更好的可伸縮性、可測試性和可維護性。

參考資料

Choosing Android Architectures: MVC, MVP, MVVM, Clean Architecture, and MVI
Android 架構演進:從 MVC 到 MVP、MVVM、MVI,Android開發如何選?
Android+Kotlin中实践Clean Architecture