MF99 coding 💻

keep learning; keep coding;

MVVM 概念篇

f:id:mouseface99:20200524154324p:plain

當一個應用開發的越來越大,越來越複雜的時候

除了開發團隊人員會越來越多之外,開發新功能,修正 Bug 的複雜度通常也是呈現指數成長

如何在產品持續營運/擴張的情況下,讓產品本身的代碼有著高度的可讀性,可維護性。

這時候第一步,通常就會搬出祖傳的 MVC 架構(或是後續變形的 MVP / MVVM / VIPER 等)

這篇會簡單介紹一下 MVC/MVVM 的概念,以及它帶來的好處以及影響,最後會帶到這套架構在 Android App 中的樣貌

但是具體的實作經驗,則會有另外一篇來介紹。

另外這篇不會提太多 MVC / MVP / MVVM / VIPER 的差異或是比較

首先,我想從最外層的結構來看一下大多數的 App 結構

f:id:mouseface99:20200524154048p:plain

大多數的 App 多少都會有跟外部 Server 的 API 溝通交換數據,然後經過 app 內的處理,再透過某些介面跟 User 互動

在這樣的結構下,你的 app 中不免的就會有著一堆用來處理這些資料/邏輯的元件或是模塊

f:id:mouseface99:20200524154508p:plain

像是

  • 資料處理
  • 流程/狀態控制
  • 用戶操作介面
  • 商業邏輯

但是,App 開發團隊通常不會直接開發 Server 端的服務,加上大一點的開發團隊通常用戶操作介面與流程控制會是由獨立的 UI/UX 部門負責。

所以,當 Server API 可能三五到頭會修改/調整, UI/UX 部門也有可能三天一小改,五天一大改,加上自己的產品團隊可能不實的也會新增功能。

如何在這些外部因素都時常在變化的情況下,能夠有一個 App 的架構,來把這些改動都限縮到只會影響特定的元件/模塊。

而各個模塊之間的溝通都是抽象的,讓彼此之間的耦合度降低(decoupling),讓各個元件專注在自己的業務上,但是調整的過程又能夠最小限度的影響到相依賴的元件。也就是關注點分離的概念(Separation of concerns)

依照上面提到的概念,基本來說就可以把 App 分成三大模塊

f:id:mouseface99:20200524155150p:plain

  • 負責資料處理的模塊 (Model)
  • 負責使用者介面/互動的模塊 (View)
  • 負責商業邏輯,並把資料與介面串接起來的模塊 (Controller)

這就形成了最經典的 Model-View-Controller (MVC) 模型

當然在實務上,或是開發者根據平台/產品特性的不同,也慢慢衍伸出了之後的 MVP (Model-View-Presenter) / MVVM (Model-View-ViewModel) 或甚至更複雜的 VIPER (View-Interactor-Presenter-Entity-Router)

不過基本上大概念還是差不多,還是把資料(Model)、操作介面(View)與控制元件區隔開,差別只在於一些細部的互動方向或是權責歸屬的不同,或是再把某一些常用的互動/交互獨立出來而已。

目前我自己的使用習慣,以及目前 Google 官方對於 Android 的開發工具的發展,MVVM 算是一個比較好上手,也有更多原生平台工具的一套架構。

MVVM 架構在 Android 上的實現,大致可以參考下面這個架構:

f:id:mouseface99:20200524155927p:plain

  • 由 Activity / Fragment 與 layout.xml 所組成的 View
  • 包含著LiveData 的 ViewModel
  • Model 的部分
    • 最上層由 Repository 來將資料層抽象開,讓 ViewModel 層不需要知道資料端的來源與處理細節
    • 下層分為外部數據(主要為 Server API)與內部數據(內部資料庫)

MVVM 的一個很重要的概念,就是 ViewModel,我的理解是他就像是一個抽象的狀態物件,可以用來描述當前產品資料的「現狀」,所以當使用者對 App 有任何互動的時候,他就會透過 repository 去跟 Model 端更新資料,然後更新 ViewModel 的狀態,再透過 Data binding 的機制來反映在使用者介面上。但是這個 ViewModel 又不需要知道目前的使用者介面是什麼樣子。

舉個簡單的例子,假設你有一個顯示個人履歷的應用,這個應用會在畫面上呈現這個 candidate 的個人資料。 但是你們非常用心,在手機等尺寸較小的螢幕上,只會顯示重點資訊(姓名、年齡、工作經歷、學歷),但是如果是在比較大的螢幕例如平板電腦,就會顯示更多附加資訊(獲獎、自傳等)

f:id:mouseface99:20200524161221p:plain

這時候 MVVM 的效果就會很明顯,因為基本上 Server API 一定會有完整的個人資訊,只差在前端要不要顯示,所以在 Repository 與 ViewModel 中就可以用同一套邏輯來處理這兩個版本,唯一的差異只在於不同尺寸的裝置上, View 要使用什麼樣不同的佈局,然後那個佈局會去提取 ViewModel 中的哪些資訊來呈現。

類似的情況也可以用來處理手機轉向的問題,Android 原生的設計,手機在 orientation change (Portrait <-> Landscape) 的時候,預設是會重啟整個 activity 的 lifecycle

但是大多數的情況,你只是需要調整一下 UI ,頂多複雜一點的會重新 load 另外一組 layout,但是資料本身應該是不會有變化(也不應該需要重新跟 Server request)

這時候透過 ViewModel 就能夠很輕鬆的將目前 App 資料的現況儲存在裡面,然後不管 UI 如何變化,調整,都只要重新再跟 ViewModel 建立 binding 之後,在呈現就可以了。


這邊先簡單介紹一下 MVC/MVVM,之後下一篇會分享一下我在之前工作的一個專案中,使用 MVVM 架構的一些經驗與心得。