Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> 從零開始的Android新項目3 - 誰告訴你MVP和MVVM是互斥的

從零開始的Android新項目3 - 誰告訴你MVP和MVVM是互斥的

編輯:關於Android編程

前言

去年5月左右的時候,筆者在逛GitHub的時候,看到了一個MVP的項目,叫做mosby,仔細看了源碼和作者介紹的文章後,發現確實有點意思,雖然會需要多寫幾個類和方法,但是解決了activity/fragment過重的問題,通過V/P分離能夠幫助提高可維護性。時至去年年底,今年年初,MVP才逐漸被大家所知,也不時看到些文章介紹其概念和實踐。

再說說MVVM (Model-View-ViewModel),在Android上對應data binding。即ViewModel到View的映射,不需要再去自己找到view,然後更新字段,而是在映射建立後直接更新ViewModel然後反映到View上。

值得一提的是,MVP和MVVM都是微軟提出的理念,最早都是在WPF裡面被應用的,只是時至今日才在Android上被真正用起來。本文不是來介紹這兩個的,所以不再贅述,講講正題。

在本系列首篇中我曾經提到過在我設計的新應用中,采用了MVP+MVVM的混合(當初也考慮過Flux,但感覺並不合適Android),後來有一次CJJ同學和我探討這個架構的時候,問到了我有沒有什麼正式的名字,我就有楞,因為這個組合和應用是我自己設計的,所以還真沒想過這個問題,Google一搜,還真有這麼個東西(見參考資料,文章寫得很棒,建議英文不錯的同學讀一讀)!

這就是本文我要介紹的東西,MVPVM (Model-View-Presenter-ViewModel)。

Quick glance

以下所有Model,並不單單指的是Bean,而是Model層,更像是repository或者business logic。

 

MVC

 

MVC: View持有Controller,傳遞事件給Controller,Controller由此去觸發Model層事件,Model更新完數據(如從網絡或者數據庫獲得數據後)觸發View的更新事件。

 

MVP

 

乍一看,MVP似乎是MVC的變種,即C的位置被P取代了,但如果我們再看一看下圖:

 

MVCP

 

其實MVP是MVC的一個wrap,C層仍然可以在那裡,代替View處理點擊事件、數據綁定、扮演ListView的觀察者,從而View可以專注於處理純視覺的一些東西。而Presenter則避免了Model直接去觸發View的更新,View徹底成為了一個被動的東西,只有Presenter告知其更新視覺,它才會去更新,比如showLoading(), showEmpty()。

 

MVVM

MVVM通過View和ViewModel的雙向綁定,讓我們可以
- 直接更新ViewModel,View會進行對應刷新
- View的事件直接傳遞到ViewModel,ViewModel去對Model進行操作並接受更新。

Why MVPVM

如果你仔細讀過Clean architecture的源碼,會發現其中已經有了ViewModel這一層。如果你熟悉DO(Domain Object),PO(Persistent Object),VO(View Object),或許了解visibility這個概念,各層只需要知道其應該知道的。這些Object代表了完全獨立不同的概念。

ViewModel層的必要性,簡單舉個例子,服務器傳來一個Date String,但我們需要顯示的是該Date到現在的相對時間描述,比如1分鐘前,2天前,為了避免在view中綁定數據時去做這個邏輯,ViewModel會代替來進行這個的轉換。

MVVM盡管確實省去了綁定數據到View的boilerplate,但
- ViewModel引用了View,從而導致ViewModel無法重用於其他View。
- 並沒有解決View層過重的問題,僅僅去掉了數據綁定,尤其對一些復雜業務邏輯的頁面。

模式的引入都是為了通過可拔插化以提高可復用性,松耦合和盡量小的接口可以給予最大的可復用性,使得組件能重組使用。

所以有了MVPVM:

MVPVM

在我的個人實踐中:
- Model: data和domain模塊組成,包含了Interactor(UseCase)、Repository、Datastore、Retrofit、Realm、DO、部分PO等。
- View: Activity/Fragment。
- Presenter:Presenter,包含了Subscriber,並通過Dagger2注入UseCase從而減輕耦合。
- ViewModel:由Model轉換而成,繼承BaseObservable或SZ喎?/kf/ware/vc/" target="_blank" class="keylink">vcnRlZExpc3SjrLTzsr+31taxvdN3cmFwwcttb2RlbKOstNO2+MiltfTBy21hcHBlcrXEYm9pbGVycGxhdGWho82ouf1EYXRhIEJpbmRpbmew87aotb14bWyhozwvcD4NCjxwPrTTUHJlc2VudGVytcRTdWJzY3JpYmVyzfnPwra8ysdSeEphdmG1xMH3ysC956Osc3RyZWFtIGluIHN0cmVhbSBvdXSho8jnufvE49StwLS+zdOm08PBy01WULvy1d9DbGVhbiBBcmNoaXRlY3R1cmWjrMTHu+G3os/W1Nm808nPVmlld01vZGVsvPLWscyrvPK1pcHLo6zNrMqxyMO0+sLrv+K4/NCho6zC37ytuPzH5c76oaM8L3A+DQo8cD6909fFv7S/tLj3uPbX6bz+1NpNVlBWTdbQtcRzdGFuZGluZ6GjPC9wPg0KPGgyIGlkPQ=="mvpvm-model">MVPVM: Model

實際對應的是Repository層,即第一篇文章中提到的data/domain module。具體的Model理論上應該是PO,但我們大部分場景並不需要PO,所以也可以是domain層的DO。

MVPVM: View

View對ViewModel不需要了解太多,這樣才能保持兩者的解耦,兩者之間的協議只需要:
- ViewModel支持View需要展示的properties。
- View實現了ViewModel的觀察者模式接口(如Listener)。

所以這裡ViewModel到View是一條虛線,而不是MVVM中的雙向實線。

MVPVM: Presenter

和在MVP一樣,Presenter站在View和Model層之間。這裡值得一提的是Presenter到ViewModel是有耦合的,因為Presenter需要把model更新到ViewModel中,也就是map行為,然後調用View的對應接口進行binding。

Presenter是MVPVM中唯一不需要解耦的,它緊緊地與View、ViewModel、Model層耦合。如果你的Presenter被多個View重用了,那你可能需要考慮它是不是更應該作為一個module,比如(第三方)登陸。

MVPVM: ViewModel

MVPVM讓ViewModel可以重用,因為它再也不是直接和特定View綁定,而僅僅作為數據到View的一個綁定用展示。ViewModel因為用戶操作而觸發的事件不再直接對Model進行操作,而由View去負責任務流。ViewModel本身基本沒有field,而是通過暴露get方法來讓data binding找到對應要顯示的property,get方法中直接調用持有的model的對應屬性get方法。

理想化的架構是通過一個mapper類進行轉換,但我想大部分的程序員面對這個工作都會抓狂,畢竟很多字段其實就是一個復制,而且對性能也有一些影響(遍歷list,new對象,一個個字段轉換,添加到新的list)。所以折中地,讓ViewModel持有Model,在get方法中直接返回對應model的具體字段,在一些特殊的field如相對時間、添加一些描述性字符的地方再去進行拼接和特殊處理。

啊,對了,說到ViewModel,Data Binding現在支持雙向綁定了哦,見https://halfthought.wordpress.com/2016/03/23/2-way-data-binding-on-android/,語法如:

不同於單向綁定的@{},使用了@={},畢竟雙向綁定這個東西還是慎用,一方面早成數據流混亂不好理解,另一方面容易出現死循環。

NO Presenter

在MVP中,我們有時候碰到的問題是,Presenter真的有必要存在嗎,尤其是一些較為靜態,沒什麼業務邏輯,只需要純展示的頁面,結果就是為了MVP而特意去創建一個Presenter。

所以Presenter不應該被強求,正如MVP中,V和C其實被並在了一起,在某些情況下(確實就是個純展示,或者很少的業務邏輯),應該允許去Presenter,並讓View承擔其任務。比如注冊頁面,我真的就只是想把用戶的輸入發到服務器驗證一下,何必非得去搞一個presenter套著呢?

我們不能永遠理想化地去選擇所謂最好的設計,在現實的必要情況下,我們要敢於捨棄,最合適的設計才是最好的設計。為此,Presenter不是強制的;為此,ViewModel並不一定通過mapper生成,而可以返回持有的DO對象對應字段。

總結

本篇講了講MVPVM及其在Android的實踐,因為時間原因來不及寫個demo來說說具體實現,歡迎大家提出意見和建議。有空的話我最近會在GitHub上寫一下demo,你如果有興趣可以follow一下等等更新: markzhai。

下集預告

Dagger匕首,比ButterKnife黃油刀鋒利得多。Square為什麼這麼有自信地給它取了這個名字,Google又為什麼會拿去做了Dagger2呢?筆者看了很多國內Dagger2的文章,但發現它們都保留在介紹API和官網翻譯的層面,無法讓讀者能明白究竟為什麼用Dagger2,又如何用好Dagger2。希望能在下一次為大家講清楚。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved