MVVM — 架構篇:書讀得多,人自然就好看起來
做新專案時,多少會想要用一些新技術,讓程式碼更精簡,架構更清楚。但是直接升級到使用 Kotlin + Lamdba,導入 Rx 和一些新元件,寫新的架構等等,對剛從 Java 入門,原本寫 MVP 架構的同學會有點吃力。
正好公司的新案子使用 Android Jetpack 的新元件來實作 MVVM 架構。這裡分享一些基礎觀念、實作方法和幾個元件的使用心得,並搭配一個 Java 寫的 App 作為範例。文章會包含以下內容:
- 介紹 MVVM 架構
- 自己寫 Observer pattern 簡單實現 MVVM 架構
- 搭配 Architecture Component 用 MVVM 架構寫一個簡單的 App,並介紹這些元件在這個架構中扮演的角色,包含:
- Android Jetpack — ViewModel
- Android Jetpack — LiveData
- Android Jetpack — Room
- Android Jetpack — Data Binding
預計至少會分成架構和實作的幾篇文章,這篇文章先聊聊架構。
設計程式的架構
實務上,服務的內容和推出的時間點是最核心的考量。
不過一般會考慮時程,人力,專案的規模,未來需求的變化等等,以在資源許可的範圍內,規劃合適的架構來進行開發。如果後續有很多功能要擴充,可以衡量是否要在前期做更有彈性的設計,多投入一些資源開發,節省未來維護的成本。
簡而言之,應用某種架構來開發程式的目的,是幫助開發者:
因應未來功能增加或是需求變更,把程式碼控制在較好維護和擴充的範圍。
有許多書在討論架構程式的方法,可以看看 Clean Code 和 Refactor 相關的書。這裡我們先來看看 M-V-X 是一個什麼樣的架構。
M - V - X 的架構
M - V - X 的架構中,把程式分成三個層級,分別有各自的職責。
- V:View — 負責定義和顯示使用者介面,並接受使用者的操作。
- M:Model — 負責處理和資料有關的事務,包括獲取資料和儲存資料。
- X:通常在 View 和 Model 間扮演橋樑的角色。
簡單描述流程:
由 View 提供畫面,當使用者在畫面上操作時,View 把事件通知 X。由 X 調用 Model 相對應的方法。Model 會從網路或本地端的儲存空間來取得資料。當 Model 取回資料時,再通知 X 或是 View 更新畫面。如下圖:
這個架構的主要想法,是把程式中不同的功能,合理的切成獨立的單元。這麼做有許多優點:
1. 明確區分職責,M, V, X 各自負責不同的功能
像是 View 和 X 不需要了解 Model 如何取資料,只要透過 Model 提供的方法,就可以透過 Model 拿到需要的資料。
2. 解耦合,避免不必要的依賴性
像是 Model 對 View 或是 X 沒有依賴性,所有元件都可以請 Model 拿資料。
3. 獨立的單元可以重複使用
像是信用卡設定頁面和付款頁面都需要使用者的信用卡資料,可使用相同 Model 的方法來取得。
4. 易於維護,可單獨修改 M, V, X 任一層
像是後端調整欄位時,可以只改 Model。調整 UI 時,只需要修改 View。
5. 易於測試
解耦合的好處之一就是方便寫測試,將功能切乾淨之後可以針對單獨的方法做測試。進一步確保修改的結果合乎預期,提高程式的穩定和可靠度。
介紹完 M- V - X 架構的基本概念,來簡單聊一下 Android 中的 MVC, MVP。
MVC (Model — View — Controller)
MVC 架構中的 M 和 V 就是上面提到的 Model 和 View,而 C 指的是 Controller,負責接收來自 View 的事件,和操作 Model 處理資料。
關於 MVC 的架構有幾種不同的說法,像是認為三者之間的關係如下圖:
Android 中的 MVC
在 Android 開發時,XML 可以當作 View,Activity 和 Fragment 則擔任 Controller。
討論
主要的好處是依職責區分單元。但這個架構有幾點可以討論:
- Activity 和 Fragment 的角色(以 Activity 為例):
由於往往需要在 Activity 中控制 View 上的元件,導致 Activity 同時需要負責 View 和 Controller 的角色。但 Activity 最重要的職責是管理介面的操作和顯示。當需求不斷增加,Activity 會很龐大,難以維護。 - View 和 Model 的關係:
如果設計成 Model 持有 View ,以在處理資料完成時通知 View 更新 UI,會增加耦合性。可以使用觀察者模式,由 View 觀察資料,讓資料在變化時主動通知 View 做相對應的更新。
MVP (Model — View — Presenter)
P 是 Presenter,負責擔任 View 與 Model 之間的橋樑。一個 Presenter 一般只對應到一個 View。
和 MVC 不同的地方是,View 和 Model 不直接互動,完全透過 Presenter 來進行溝通,成為兩個獨立的單元。
Android 中的 MVP
使用 MVP 架構來開發 Android App 時,會把 Activity 與 Fragment 當作 View,專心負責控制使用者介面。程式的邏輯則放在 Presenter 中。
討論
相較於 MVC 架構,MVP 架構更重視元件之間的分離,分幾個點做討論。
- 職責的區分:
由於邏輯放到 Presenter 中,Activity 和 Fragment 可以專心處理自己的生命週期,和負責管理介面的操作和顯示。 - View 和 Model 完全獨立:
達到較高的可維護性,方便重用,利於單元測試。 - 使用 interface 互相操作:
實作上會在各元件間加入 interface,用 interface 來規範方法。彼此間互相注入 interface 來進行操作來降低耦合性。相對的,由於依賴 interface 提供的方法來操作,需求修改時,interface 和實作需要一起修改。 - 一個 Presenter 只處理一個 View 的邏輯:
為了達到一個 Presenter 只處理一個 View 的邏輯,可能會有一些重複的 Presenter 和 interface。
遇到比較複雜的業務需求時,有些有趣的做法可以保持 MVP 的精神,又可以儘量降低耦合性,有機會再來聊聊。接下來介紹本篇的重點 MVVM。
MVVM (Model — View — ViewModel)
MVVM 中的 VM 指的是 ViewModel,一樣負責接收從 View 傳來的使用者操作事件,並使用 Model 提供的方法來處理資料。
最主要的差異在於由數據來驅動 View 的更新,當資料改變時,UI 自動更新(一般用觀察者模式實現)。
資料改變時 UI 自動更新的現象,舉一個生活上的例子大家會很有感覺:
— 書讀得多,人自然就好看起來 —
Android 中的 MVVM
Google 在 2017 出的 Architecture Component 中有幾個元件可以方便的實現 MVVM 架構。有興趣可以先看看 sunflower 範例或是 CodeLab。之後會介紹這些元件的用法和角色。
討論
MVVM 架構有幾個主要的優點:
- 資料驅動:
事件都透過資料的變化來觸發,資料成為最關鍵的因素。 - 下層元件不需要知道上層元件:
在 MVC / MVP 中,都需要有 View 的引用來更新 UI。但在 MVVM 中,由View 主動觀察資料,在資料變化後收到通知,而自動更新。 ViewModel 不需要知道 View 是誰。 - 職責分離:
ViewModel 中可以減少大量通知 View 的程式碼,專心的管理流程。
Activity 和 Fragment 不用儲存資料狀態,可以專心管理介面的操作和顯示,並妥善控制生命週期。
下一篇文章中會附上幾種不同的實作範例。包括自己寫 Observer Pattern 來實現 View 觀察資料,和使用 Architecture Component 來實作的方法。