淺談MVP架構(gòu)及開(kāi)發(fā)模式
MVC or MVP Pattern – Whats the difference?
Android App的設(shè)計(jì)架構(gòu):MVC,MVP,MVVM與架構(gòu)經(jīng)驗(yàn)談
Android中的MVP
【譯】Android開(kāi)發(fā)中的MVP架構(gòu)
介紹Activity是上帝類(lèi)
首先,讓我們思考下為什么在Android開(kāi)發(fā)中如此迫切地需要一個(gè)清晰的架構(gòu)。
該段摘自“代碼大全第二版”
“避免創(chuàng)建神類(lèi)。避免創(chuàng)建無(wú)所不知,無(wú)所不能的上帝類(lèi)。如果一個(gè)類(lèi)需要花費(fèi)時(shí)間從其他類(lèi)中通過(guò)Get()和Set()檢索數(shù)據(jù)(也就是說(shuō),需要深入業(yè)務(wù)并且告訴它們?nèi)绾稳プ觯允欠駪?yīng)該把這些功能函數(shù)更好的組織到其它類(lèi)而不是上帝類(lèi)中。(Riel 1996)”
上帝類(lèi)的維護(hù)成本很高,你很難理解正在進(jìn)行的操作,并且難以測(cè)試和擴(kuò)展,這就是為什么要避免創(chuàng)建上帝類(lèi)的黃金法則。
然而,在Android開(kāi)發(fā)中,如果你不考慮架構(gòu)的話(huà),Activity類(lèi)往往會(huì)越來(lái)越大。這是因?yàn)椋贏ndroid中,允許View和其它線(xiàn)程共存于Activity內(nèi)。其實(shí)最大的問(wèn)題莫過(guò)于在****Activity****中同時(shí)存在業(yè)務(wù)邏輯和UI****邏輯。這會(huì)增加測(cè)試和維護(hù)的成本。
- UI + 業(yè)務(wù)邏輯
- 多線(xiàn)程
- 意大利面代碼:指一個(gè)代碼的控制結(jié)構(gòu)復(fù)雜、混亂而難以理解
- 難以測(cè)試
這是為什么需要清晰架構(gòu)的原因之一。不僅會(huì)造成Activity的臃腫,還會(huì)引起其它問(wèn)題,如使Activity和Fragment的生命周期變的復(fù)雜,以及數(shù)據(jù)綁定等。
什么是MVP?
MVP代表Model,View和Presenter。
- View層負(fù)責(zé)處理用戶(hù)事件和視圖部分的展示。在Android中,它可能是Activity或者Fragment類(lèi)。
- Model層負(fù)責(zé)訪(fǎng)問(wèn)數(shù)據(jù)。數(shù)據(jù)可以是遠(yuǎn)程的Server API,本地?cái)?shù)據(jù)庫(kù)或者SharedPreference等。
- Presenter層是連接(或適配)View和Model的橋梁。
下圖是基于MVP架構(gòu)的模式之一。View是UI線(xiàn)程。Presenter是View與Model之間的適配器。UseCase或者Domain在Model層中,負(fù)責(zé)從實(shí)體獲取或載入數(shù)據(jù)。依賴(lài)規(guī)則如下:
關(guān)鍵是,高層接口不知道底層接口的的細(xì)節(jié),或者更準(zhǔn)確地說(shuō),高層接口不能,不應(yīng)該,并且必須不了解底層接口的細(xì)節(jié),是(面向)抽象的,并且是細(xì)節(jié)隱藏。
同心圓將軟件劃分為不同的區(qū)域,一般的,隨著層級(jí)的深入,軟件的等級(jí)也就越高。外圓是實(shí)現(xiàn)機(jī)制,內(nèi)圓是核心策略。
Entities:
- 可以是一個(gè)持有方法函數(shù)的對(duì)象
- 可以是一組數(shù)據(jù)結(jié)構(gòu)或方法函數(shù)
- 它并不重要,能在項(xiàng)目中被不同應(yīng)用程序使用即可
Use Cases
- 包含特定于應(yīng)用程序的業(yè)務(wù)規(guī)則
- 精心編排流入Entity或者Entity流出的數(shù)據(jù)
- 指揮Entity直接使用項(xiàng)目范圍內(nèi)的業(yè)務(wù)規(guī)則,從而實(shí)現(xiàn)Use Case的目標(biāo)
Presenter,Controllers
- 將Use Case和Entity中的數(shù)據(jù)轉(zhuǎn)換成格式最方便的數(shù)據(jù)
- 外部系統(tǒng),如數(shù)據(jù)庫(kù)或網(wǎng)頁(yè)能夠方便的使用這些數(shù)據(jù)
- 完全包含GUI的MVC架構(gòu)
External Interfaces,UI,DB
- 所有的細(xì)節(jié)所在
- 如數(shù)據(jù)庫(kù)細(xì)節(jié),Web框架細(xì)節(jié),等等
MVC、MCP與MVVM的關(guān)系
MVC—>MVP
MVP從更早的MVC演變過(guò)來(lái),與MVC有一定的相似性,MVP的Presenter是框架的控制者,承擔(dān)了大量的邏輯操作,而MVC的Controller更多時(shí)候承擔(dān)一種轉(zhuǎn)發(fā)的作用。因此在App中引入MVP的原因,是為了將此前在Activity中包含的大量邏輯操作放到控制層中,避免Activity的臃腫。
兩種模式的主要區(qū)別:
- (最主要區(qū)別)View與Model并不直接交互,而是通過(guò)與Presenter交互來(lái)與Model間接交互。而在MVC中View可以與Model直接交互。
- 通常View與Presenter是一對(duì)一的,但復(fù)雜的View可能綁定多個(gè)Presenter來(lái)處理邏輯。而Controller是基于行為的,并且可以被多個(gè)View共享,Controller可以負(fù)責(zé)決定顯示哪個(gè)View
- Presenter與View的交互是通過(guò)接口來(lái)進(jìn)行的,更有利于添加單元測(cè)試。
因此我們可以發(fā)現(xiàn)MVP的優(yōu)點(diǎn)如下:
- 模型與視圖完全分離,我們可以修改視圖而不影響模型
- 可以更高效地使用模型,因?yàn)樗械慕换ザ及l(fā)生在一個(gè)地方——Presenter內(nèi)部
- 我們可以將一個(gè)Presenter用于多個(gè)視圖,而不需要改變Presenter的邏輯。這個(gè)特性非常的有用,因?yàn)橐晥D的變化總是比模型的變化頻繁
- 如果我們把邏輯放到Presenter中,那么我們就可以脫離用戶(hù)接口來(lái)測(cè)試這些邏輯(單元測(cè)試)。
MVVM
MVVM可以算是MVP的升級(jí)版,其中VM是ViewModel的縮寫(xiě),ViewModel可以理解成是View的數(shù)據(jù)模型和Presenter的合體,ViewModel和View之間的監(jiān)護(hù)通過(guò)Data Binding完成,而Data Binding可以實(shí)現(xiàn)雙向的交互,這就使得視圖和控制層之間的耦合程度進(jìn)一步降低,關(guān)注點(diǎn)分離更為徹底,同時(shí)減輕了Activity的壓力。
MVC,MVP還是MVVM?
那么,哪一個(gè)才是最好的呢?哪一個(gè)比其他的更優(yōu)秀呢?我能只選擇一個(gè)嗎?
答案是,NO。
這些模式的動(dòng)機(jī)都是一樣的。那就是如何避免復(fù)雜混亂的代碼,讓執(zhí)行單元測(cè)試變得容易,創(chuàng)造高質(zhì)量應(yīng)用程序。就這樣。
當(dāng)然,遠(yuǎn)不止這三種架構(gòu)模式。而且任何一種模式都不可能是銀彈,他們只是架構(gòu)模式之一,不是解決問(wèn)題的唯一途徑。這些只是方法、手段而不是目的、目標(biāo)。
利與弊
OK,讓我們回到MVP架構(gòu)上。剛剛我們了解了什么是MVP,討論了MVP以及其它熱門(mén)架構(gòu),并且介紹了MVC,MVP和MVVM三者間的不同。這是關(guān)于MVP架構(gòu)利與弊的總結(jié)。
利
- 可測(cè)試(TDD)
- 可維護(hù)(代碼復(fù)用)
- 容易R(shí)eview
- 信息屏蔽
弊 - 冗余的,尤其是小型App開(kāi)發(fā)
- (有可能)額外的學(xué)習(xí)曲線(xiàn)
- 開(kāi)始編寫(xiě)代碼之前需要時(shí)間成本(但是我敢打賭,設(shè)計(jì)架構(gòu)是所有項(xiàng)目開(kāi)發(fā)所必須的)
基于AOP的框架設(shè)計(jì)
** AOP(Aspect-Oriented Programming,面向切面編程)**
誕生于上個(gè)世紀(jì)90年代,是對(duì)OOP(Object-Oriented Programming,面向?qū)ο缶幊蹋┑难a(bǔ)充和完善。OOP引入封裝、繼承和多態(tài)性等概念來(lái)建立一種對(duì)象層次結(jié)構(gòu),用以模擬公共行為的一個(gè)集合。當(dāng)我們需要為分散的對(duì)象引入公共行為的時(shí)候,OOP則顯得無(wú)能為力。也就是說(shuō),OOP允許你定義從上到下的關(guān)系,但并不適合定義從左到右的關(guān)系。例如日志功能。日志代碼往往水平地散布在所有對(duì)象層次中,而與它所散布到的對(duì)象的核心功能毫無(wú)關(guān)系。對(duì)于其他類(lèi)型的代碼,如安全性、異常處理和透明的持續(xù)性也是如此。這種散布在各處的無(wú)關(guān)代碼被稱(chēng)為橫切(Cross-Cutting)代碼,在OOP設(shè)計(jì)中,它導(dǎo)致了大量代碼的重復(fù),而不利于各個(gè)模塊的重用。而AOP技術(shù)則恰恰相反,它利用一種稱(chēng)為“橫切”的技術(shù),剖解開(kāi)封裝的對(duì)象內(nèi)部,并將哪些影響了多個(gè)類(lèi)的公共行為封裝到一個(gè)可重用模塊,并將其命名為“Aspect”,即方面。所謂“方面”,簡(jiǎn)單地說(shuō),就是將那些與業(yè)務(wù)無(wú)關(guān),卻為業(yè)務(wù)模塊所共同調(diào)用的邏輯或責(zé)任封裝起來(lái),便于減少系統(tǒng)的重復(fù)代碼,降低模塊間的耦合度,并有利于未來(lái)的可操作性和可維護(hù)性。
AOP在Android中的使用
AOP把軟件系統(tǒng)分為兩個(gè)部分:核心關(guān)注點(diǎn)和橫切關(guān)注點(diǎn)。業(yè)務(wù)處理的主要流程是核心關(guān)注點(diǎn),與之關(guān)系不大的部分是橫切關(guān)注點(diǎn)。橫切關(guān)注點(diǎn)的一個(gè)特點(diǎn)是,他們經(jīng)常發(fā)生在核心關(guān)注點(diǎn)的多處,而各處都基本相似。AOP的作用在于分離系統(tǒng)中的各種關(guān)注點(diǎn),將核心關(guān)注點(diǎn)和橫切關(guān)注點(diǎn)分離開(kāi)來(lái)。在Android App中,哪些是我們需要的橫切關(guān)注點(diǎn)?個(gè)人認(rèn)為主要包括一下幾個(gè)方面:Http,SharedPreferences,Json,Xml,F(xiàn)ile,Device,System,Log,格式轉(zhuǎn)換等。Android App的需求差別很大,不同的需求橫切關(guān)注點(diǎn)必然是不一樣的。一般的App工程中應(yīng)該有一個(gè)Util Package來(lái)存放相關(guān)的切面操作,在項(xiàng)目多了之后可以將其中使用較多的Util封裝為一個(gè)Jar包供工程調(diào)用。
在使用MVP和AOP對(duì)App進(jìn)行縱向和橫向的切割之后,能夠使得App整個(gè)的結(jié)構(gòu)更清晰合理,避免局部的代碼臃腫,方便開(kāi)發(fā)測(cè)試以及后續(xù)的維護(hù)。