Android中的MVC MVP MVVM框架模式

前言

談起MVC,MVP和MVVM這三個最耳熟能詳的Android框架,相信大家對它們都不陌生,但在實際的情況下,我們很難去界定和定義MVC、MVP和MVVM,從本質上來講他們的目的都是分層解耦,結構上也都是分為三部分,而且三者都有著一些相似的特性,對于一些新手來說,可能理解和區分它們三個都有困難,更別說在實際的項目中應用了,現在我們來分別理解它們,并看看它們之間有什么相同點和不同點!

1. MVC 框架模式

MVC的全名是Model View Controller,即-模型(model)-視圖(view)-控制器(controller),它是一種軟件的設計典范,它通過“業務邏輯、數據、界面顯示”的分離手段來組織代碼。

早期的MVC 框架模式

MVC框架模式最早由Trygve Reenskaug 于1978年在Smalltalk-80系統上首次提出,我們來看下當時Reenskaug為解析MVC模式所繪制的一個例圖:

Reenskaug 繪制的MVC關系圖

于此同時,Reenskaug也給出了自己對MVC的介紹:

Model: Model可以是一個獨立的對象,也可以是一系列對象的集合體;

View: View是Model中一些重要數據在視覺上的體現;

Controller: Controller用于連接User和System,比如當Controller接受到用戶的輸出時,會將其轉換成合適的事件消息,并將該事件消息傳遞給一個或多個View;

比較經典的 MVC 框架模式

MVC后來的發展比較多元化,也演化出很多的小分支,不過不管其怎么改變,Model、View、Controller的結構都沒有改變.

MVC作為最出名并且應用最廣泛的架構模式,其并沒有一個明確的定義,網上流傳的 MVC 架構圖也是形態各異,個人認為比較經典的MVC結構如下圖:

MVC經典結構.png

模型層(Model):
程序需要操作的數據或信息(系統中的業務邏輯部分),比如數據的存儲、網絡請求等,同時Model與View也存在一定的耦合,通過某種事件機制(如觀察者模式)通知View狀態改變或更新;Model還會接收來自Controller的事件,Model也會允許View 查詢相關數據以顯示自身狀態.


視圖層(View):
直接面向于最終用戶的視圖層,并提供給用戶操作界面,是Model的具體表現形式,是程序的外殼。該層只負責展示數據,主要是由各種GUI組件組成,同時響應用戶的交互行為并觸發Controller的邏輯,View還會通過在Model中注冊事件監聽Model的改變以此來刷新自身并展示給用戶(如監聽Model中的Bitmap圖像,當Bitmap加載或清除時,對應的View顯示不同的圖像).


控制器層(Controller):
Controller由View根據用戶行為觸發,并響應來自View的交互,然后根據View的事件邏輯修改對應的Model,Controller并不關心View如何展示數據或狀態,而是通過修改Model并由Model的事件機制來觸發View的刷新.

下圖展示了MVC程序框架在Android應用程序中的使用,以及各個層次使用哪些組件擔當:

MVC層次圖

使用MVC的目的就是將M和V的實現代碼分離,從而使同一個程序可以使用不同的表現形式,而Controller存在的目的則是確保M改變,V應該同步更新。

Android應用程序中,MVC框架是如何實現的?都充當什么角色?

  1. View接受用戶的交互請求;
  2. View將請求轉交給Controller;
  3. Controller(用戶做的動作比如:update數據,刪除指定名字的學生等等)操作Model進行數據更新(根據用戶指示,執行底層的數據動作等等);
  4. 數據更新之后,Model通知View數據變化;
  5. View顯示更新之后的數據;
MVC流程圖

M層適合做一些業務邏輯處理,比如數據庫存取操作、網絡操作、復雜的算法等耗時操作;

V層顯示數據部分,XML布局可以視為是V層,顯示Model層的數據結果;

C層適合使用Activity擔當,Android中Activity用于處理用戶交互問題(發起業務請求),讀取用戶輸入(等待業務處理結果),響應用戶點擊等等事件。

MVC 的優缺點及適用場景

優點:

  1. 耦合性低,MVC本質是分層解耦,將表現層與表現邏輯很好的分離,減少模塊代碼之間的相互影響.
  2. 可擴展性好。由于耦合性低,添加需求,擴展代碼就可以減少修改之前的代碼,降低bug的出現率.
  3. 模塊職責劃分明確。主要劃分層M,V,C三個模塊,利于代碼的維護.

缺點:

  1. View層和Model層是相互可知的,這意味著兩層之間存在耦合;
  2. 在Android開發中,xml作為View層,控制能力實在太弱了,只能把代碼寫在Activity中,造成了Activity既是Controller層,又是view層這樣一個窘境,而且Activity并不是一個標準的MVC模式中的Controller,它的首要職責是加載應用的布局和初始化用戶界面,并接受和處理來自用戶的操作請求,進而作出響應。隨著應用內界面及其邏輯的復雜度不斷提升,Activity類的職責不斷增加,以致變得龐大而臃腫。

適用場景:適用于功能較少、業務邏輯簡單、界面不復雜的小型項目。

上面我們花了不少的時間來談論MVC,我個人覺得是很有必要的,能很好的理解MVC,才能更快的理解MVP和MVVM,他們都是MVC的演化版本。

2. MVP 框架模式

MVP全稱Model View Presenter,它是MVC的一個演化版本,常用MVP結構如下圖:

常用MVP模式.png

數據的存取(Model):
Model角色主要是提供數據的存取功能,Presenter需要通過Model層存取、獲取數據,Model就像一個數據倉庫,更直白說Model是封裝了數據DAO、網絡獲取數據的角色,或者是兩種數據獲取方式的集合。


用戶界面(View):
View通常指Activity、Fragment或者某個View控件,它含有一個Presenter成員變量。通常View需要實現一個邏輯接口,將View上的操作轉交給Presenter進行實現,最后,Presenter將調用View邏輯接口將結果返回給View元素。


交互中間人(Presenter):
Presenter主要作為View和Model的橋梁,它從Model層檢索數據后,返回給View,使得View和Model之間沒有耦合,也將業務邏輯從View層上抽離出來。

MVP并沒有一個標準的模式,它有很多的實現方式,但無論如何,我們只要保證:通過Presenter將View和Model解耦合、降低類型復雜度、各個模塊可以獨立測試、獨立變化,這就是正確的方向;

我個人覺得MVP模式最重要的實現方式是:分離顯示層和邏輯層,使它們通過接口進行通信,降低耦合;理想的MVP模式可以實現一份邏輯代碼搭配不同的顯示界面,因為它們之間不依賴具體實現,而是依賴于抽象。

Android應用程序中,MVP框架是如何實現的?都充當什么角色?

  1. View接受用戶的交互請求;
  2. View通過Presenter暴露接口將請求轉交給Presenter來進行處理;
  3. Presenter 通過Model暴露接口對Model進行操作和更新;
  4. Model改變后,可以將改變的信息發送至Presenter;
  5. Presenter通過View暴露接口對View進行視圖更新;

M層提供數據上的存取,比如數據庫存取操作、網絡操作、復雜的算法等操作;

V層負責顯示數據部分,顯示Model層的數據結果,Activity、Fragment或者某個View控件可以視為是V層,;

P層更多的是作為V層和M層的橋梁,主要負責相關的業務邏輯。

MVP還有很多演化版本和分支版本,它不存在標準和固定的模式,只要是符合上面提到的實現方式,它就是合理的。

MVP的優缺點及適用場景

為什么說MVP是MVC的演化版本,因為MVP很好的解決了MVC的一些弊端。

優點:

  1. 通過P層的轉接能避免業務邏輯被塞進View中,有效的降低View的復雜性;
  2. View層和Model層完全解耦,他們之間互不干擾,帶來了良好的可拓展性、可測試性,保證了系統的靈活性和整潔性;
  3. Activity、Fragment等僅僅為View層,不會再出現MVC那種Activity、Fragment即做View層又做Controller層的窘境,View層及Model層大大提高復用性;
  4. 我們可以將一個Presenter用于多個視圖,而不需要改變Presenter的邏輯。這個也是我上面提到的理想的MVP模式;
  5. 由于各層分工明確,極便于單元測試;

缺點:

  1. MVP是以UI為驅動的模型,更新UI時需要保證能獲取到控件的引用,同時更新UI時也要考慮當前是否是UI線程;
  2. 復雜的業務同時也可能會導致P層太大,代碼臃腫的問題依然不能解決;
  3. MVP通過接口進行交互的,接口粒度不好設計和控制。粒度太小,就會存在大量接口;粒度太大,解耦效果不好;
  4. MVP數據都是被動地通過UI控件做展示,但是由于數據的時變性,我們更希望數據能轉被動為主動,使數據能更有活性,由數據來驅動UI;

適用場景:適用于界面復雜、復用性高、功能中等、業務邏輯中等或是簡單的項目。實際上MVP的思想很適合用于復雜的界面上,我們完全可以在項目中某一部分上使用MVP的思想去靈活實現(當然你總監或是領導看到你這樣實現打你請不要出賣我,那是你沒溝通好)

例子:Android源碼NavigationView、官方MVP例子:todo?mvp

Android中MVP實現需要注意的點

綜上所述,MVP有很多的優點,但在Android中Activity、Fragment作為View層時,它不僅僅只是View那么簡單,它還存在自身的生命周期;以下是我總結的幾個需要注意的點,非常歡迎補充:

  1. 更新UI時要考慮當前執行的線程是否是UI線程,也要考慮Activity的生命周期,做好Activity已被摧毀的處理;
  2. P層需要執行耗時操作,而P層持有Activity、Fragment的強引用,在耗時操作結束前Activity、Fragment已被摧毀了,但由于P層一直持有Activity、Fragment對象,導致Activity、Fragment無法被回收引起內存泄露;這里建議使用弱引用和Activity、Fragment的聲明周期來處理;

3. MVVM 框架模式

說到Android MVVM,相信大家都會想到Google 2015年推出的DataBinding框架。然而兩者的概念是不一樣的,不能混為一談。MVVM是一種架構模式,而DataBinding是一個實現數據和UI綁定的框架,是構建MVVM模式的一個工具。


MVVM全稱Model View ViewModel,你可以把MVP看做是MVVM的一個改進版本,常用的MVVM結構如下圖:

MVVM結構圖

Model:
Model主要是封裝數據存儲或操作的一些邏輯,還會提供一系列的實體類用于UI綁定,ViewModel 則會在修改這些數據后將數據改變告訴View層并使UI更新。


View:
View用于處理界面的邏輯且不參與業務邏輯相關的操作,只負責顯示由ViewModel提供的數據,View層不做任何業務邏輯、不涉及操作數據、不處理數據,UI和數據嚴格的分開,對應于Activity和XML。


ViewModel:
ViewModel層做的事情剛好和View層相反,ViewModel只做和業務邏輯和業務數據相關的事,不做任何和UI相關的事情,ViewModel層不會持有任何控件的引用,更不會在ViewModel中通過UI控件的引用去做更新UI的事情。ViewModel就是專注于業務的邏輯處理,做的事情也都只是對數據的操作(這些數據綁定在相應的控件上會自動去更改UI)。同時DataBinding框架已經支持雙向綁定,讓我們可以通過雙向綁定獲取View層反饋給ViewModel層的數據,并對這些數據進行操作。

MVVM的目標和思想與MVP類似,利用數據綁定(Data Binding)、依賴屬性(Dependency Property)、命令(Command)、路由事件(Routed Event)等新特性,打造了一個更加靈活高效的架構,
在常規的開發模式中,數據變化需要更新UI的時候,需要先獲取UI控件的引用,然后再更新UI,獲取用戶的輸入和操作也需要通過UI控件的引用,但在MVVM中,這些都是通過數據驅動來自動完成的,數據變化后會自動更新UI,UI的改變也能自動反饋到數據層,數據成為主導因素。這樣MVVM層在業務邏輯處理中只要關心數據,不需要直接和UI打交道,在業務處理過程中簡單方便很多。

MVVM模式中,數據是獨立于UI的,數據和業務邏輯處于一個獨立的ViewModel中,ViewModel只需要關注數據和業務邏輯,不需要和UI或者控件打交道。UI想怎么顯示數據都由UI自己決定,ViewModel不涉及任何和UI相關的事,也不持有UI控件的引用。即便是控件改變了(比如:TextView換成EditText),ViewModel也幾乎不需要更改任何代碼。它非常完美的解耦了View層和ViewModel,解決了上面我們所說的MVP的痛點。

MVVM的優缺點及適用場景

優點:

  1. 低耦合,數據和業務邏輯處于一個獨立的ViewModel中,ViewModel只需要關注數據和業務邏輯,不需要和View層打交道。
  2. 可重用性,你可以把一些視圖邏輯放在一個ViewModel里面,讓很多view重用這段視圖邏輯。
  3. 可分開獨立開發,MVVM的分工是非常明顯的,由于View和ViewModel之間是松散耦合的:一個是處理業務和數據、一個是專門的UI處理。所以,完全可以由兩個人分工來做,一個做UI(XML和Activity)一個寫ViewModel,效率更高。
  4. 由于各層分工明確,極便于單元測試;
  5. 相對于MVP而言,MVVM不需要我們手動的處理大量的View和Model相關操作,也非常完美的解耦了View層和ViewModel;

缺點:

  1. 數據綁定使得 Bug 很難被調試,你看到界面異常了,有可能是你 View 的代碼有 Bug,也可能是 Model 的代碼有問題。數據綁定使得一個位置的 Bug 被快速傳遞到別的位置,要定位原始出問題的地方就變得不那么容易了。
  2. 對于過大的項目,數據綁定需要花費更多的內存,而對與過于簡單的界面,使用MVVM無異是殺雞用牛刀,某種意義上來說,我認為就是數據綁定使得 MVVM 變得復雜和難用了。

適用場景:我自身沒有在實際項目中應用過MVVM,這里不好定義使用場景,希望在實際項目中使用過的朋友可以告知,但MVVM是不適用于簡單的界面和極度復雜的界面,在界面簡單的情況下,MVVM反而將我們的邏輯復雜化了,而界面元素過多,相對應的ViewModel的構建和維護成本就會變的很高,不利于項目的發展。

4. MVC MVP MVVM的異同

MVC MVP MVVM
Model 典型的MVC中的Model與View存在耦合,一般情況下Model會以事件監聽的方式將數據的改變告知View層 MVP下的Model與View沒有任何的直接的耦合,其更像一個對外提供數據的倉庫 MVVM中的Model與MVP類似,只負責數據的封裝,不同的是還會配合Binder綁定數據變化的監聽
View 典型MVC中的View層比較簡單,主要是界面相關的邏輯,為了方便查詢數據和監聽數據的變化,View還會與Model有一定的耦合 Model與View沒有直接聯系,同時在一些情況下View還會依賴于接口進一步解耦 MVVM中的View除了處理UI相關的邏輯外還會配合Binder綁定UI變化的監聽

在實際的情況下,我們很難去界定MVC、MVP和MVVM,他們三者有著一些相似的特性,從本質上來講他們的目的都是分層解耦,結構上也都是分為3部分,其中都有View層和Model層,但是其職責又有一定的不同:

MVC MVP MVVM
Model 典型的MVC中的Model與View存在耦合,一般情況下Model會以事件監聽的方式將數據的改變告知View層 MVP下的Model與View沒有任何的直接的耦合,其更像一個對外提供數據的倉庫 MVVM中的Model與MVP類似,只負責數據的封裝,不同的是還會配合Binder綁定數據變化的監聽
View 典型MVC中的View層比較簡單,主要是界面相關的邏輯,為了方便查詢數據和監聽數據的變化,View還會與Model有一定的耦合 Model與View沒有直接聯系,同時在一些情況下View還會依賴于接口進一步解耦 MVVM中的View除了處理UI相關的邏輯外還會配合Binder綁定UI變化的監聽

注:這里的Binder不是Android的Binder機制,這里是指負責View和Model之間的數據邏輯操作的處理;

Controller Presenter ViewModel
Controller主要處理用戶的交互邏輯,其根據View層的交互事件的不同,調用不同的Model層邏輯進行相關的數據操作或者直接進行View層的切換。對于Controller而言,并不關心View如何展示數據或狀態,Controller只會通過修改對應的Model層,并由Model的事件機制來觸發View的刷新. Presenter的作用類似MVC中的Controller,但是其會反作用于View層,Model層的數據更新會先反饋到Presenter,再由Presenter處理并決定是否刷新以及刷新那個View,也就是說Presenter完全隔離View層和Model層,是一個名副其實的中間人角色. ViewModel相對于Controller和Presenter的職責會更少,它將原本Controller和Presenter于View和Model交互的業務邏輯抽取交由Binder負責,Binder一般都會有對應的框架實現,對于開發者而言自己實現ViewModel的部分就相對變少了.

從上面的架構結構圖,我們可以看到,MVC、MVP和MVVM三者中Model和View在演化的過程中遵循這么一個理念:耦合 -> 解耦 -> 實用,在MVC中View和Model還存在一定的耦合,而在MVP中則徹底拋開這個耦合并依賴于接口進一步解耦,到了MVVM,MVP該做的都做的差不多了,唯一不好的就是很多時候View和Model之間有大量重復的數據交互操作,MVVM則提出將這些操作進一步提取由框架來承擔。

除了Model和View外,MVC、MVP和MVVM都將業務邏輯的處理單獨放置在Controller、Presenter和ViewModel中,雖然名稱不同但它們的本質都是一樣的,只是在具體的業務處理以及職責劃分上會有一定的差異:

Controller Presenter ViewModel
Controller主要處理用戶的交互邏輯,其根據View層的交互事件的不同,調用不同的Model層邏輯進行相關的數據操作或者直接進行View層的切換。對于Controller而言,并不關心View如何展示數據或狀態,Controller只會通過修改對應的Model層,并由Model的事件機制來觸發View的刷新. Presenter的作用類似MVC中的Controller,但是其會反作用于View層,Model層的數據更新會先反饋到Presenter,再由Presenter處理并決定是否刷新以及刷新那個View,也就是說Presenter完全隔離View層和Model層,是一個名副其實的中間人角色. ViewModel相對于Controller和Presenter的職責會更少,它將原本Controller和Presenter于View和Model交互的業務邏輯抽取交由Binder負責,Binder一般都會有對應的框架實現,對于開發者而言自己實現ViewModel的部分就相對變少了.

從MVC到MVP再到MVVM,我們可以看到整個框架模式的演進,因為MVC的不足到MVP的出現,對MVP的缺陷彌補到MVVM的誕生,三者是一個循序漸進的過程,在以后必定會有更多更好的框架模式的出現。

5. 總結

對于MVC MVP MVVM框架模式的學習,一定要實踐,一定要實踐,一定要實踐,重要的事情說三遍,無論你看了多少文章,構思了多少遍,如果沒有實踐,都很難體會到這些框架里的思想及具體的實現方式;我個人覺得比較好的學習方式是:先了解和理解各個框架模式的發展歷程和特點,然后有意識的寫幾個例子(如登錄功能、注冊功能),好好的對比自己平時的代碼與用了相關框架模式的代碼之間的區別,加深對不同框架模式的理解,最后也是最好能結合相關的實際項目去實踐,當你使用該框架模式時請記得問問自己為什么使用這個模式?而不是其他模式?它能解決什么問題?是否是最優解?

通過設計使程序模塊化,做到模塊內部的高聚合和模塊之間的低耦合,提高程序開發的效率,并且更容易進行后續的測試以及定位問題,對于不同量級及類型的應用,具體架構的實現方式必然是不同的,我個人覺得最重要的還是能靈活的運用各個框架模式的思想,而不是一定要使用特定的某個框架模式,或者我們可以變通下混合使用,但在實際開發中,特別是團隊開發中,還要要遵守大家約定好的規則執行。

查考文獻:

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,224評論 6 529
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 97,916評論 3 413
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,014評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,466評論 1 308
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,245評論 6 405
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,795評論 1 320
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 42,869評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,010評論 0 285
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,524評論 1 331
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,487評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,634評論 1 366
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,173評論 5 355
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 43,884評論 3 345
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,282評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,541評論 1 281
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,236評論 3 388
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,623評論 2 370