猶豫了很久,還是決定靜下心來寫一寫自己實踐的MVP模式相關的內容,我怕我再不寫,就要丟失了那些採坑的記憶,就要丟失了寫博客的習慣,最可怕的是,再不寫,可能就幫不了那些真正想要了解和使用MVP的童鞋們了。
我知道有很多很多關於MVP的文章,甚至還有很多很多不同的MVP擴展,看我依然覺得我要繼續寫下去,因爲我堅定地認爲,我也可以有別具一格的MVP。
這個系列大概有三個部分:
如果我的這篇文章是你第一次接觸MVP的文章的話,那麼,我就不得不把前輩們的乾貨分享給你了,因爲很多原理的解析都源於這些乾貨裏
乾貨在於精而不在於多,所以從現在開始,忘掉乾貨,讓我們開始今天的主題吧。
一.什麼是MVP
1.1MVP的定義
MVP,全稱 Model-View-Presenter。
MVP(Model-View-Presenter,模型-視圖-表示器)模式是由IBM開發出來的一個針對C++和Java的編程模型,大概出現於2000年,是MVC模式的一個變種,主要用來隔離UI、UI邏輯和業務邏輯、數據。也就是說,MVP 是從經典的模式MVC演變而來,它們的基本思想有相通的地方:Controller/Presenter負責邏輯的處理,Model提供數據,View負責顯示。
1.2爲什麼使用MVP
Kiss原則(Keep It Stupid Simple)
說到MVP,那麼就不得不提一提他的前輩——MVC。對於MVC模式,你可能早已爛熟於心,然而,我相信,很多程序員按照MVC模式寫出來的程序大概是這個樣子的:
這張圖片讓你想到了什麼?是不是自己寫過千萬遍的---->
“所有的事物都被連接到一起”的替代品是一個萬能對象(god object)。
god object是十分複雜的,他的每一個部分都不能重複利用,無法輕易的測試、或者調試和重構。
但是我們把剛纔的那張圖片裏的View-Data按照MVP模式重新整理過後
複雜的任務被分成細小的任務,並且很容易解決。越小的東西,bug越少,越容易debug,更好測試。在MVP模式下的View層將會變得簡單,所以即便是他請求數據的時候也不需要回調函數。View邏輯變成十分直接——通過接口,告訴我你想要的樣子,我便是你想要的樣子。View和Data不在需要直接關聯,不關聯就意味着Data和View的重用性變得更高。
後臺任務
當你編寫一個Actviity、Fragment、自定義View的時候,你會把所有的和後臺任務相關的方法寫在一個靜態類或者外部類中。這樣,你的Task不再和Activity聯繫在一起,這既不會導致內存泄露,也不依賴於Activity的重建。
這裏有若干種方法處理後臺任務,但是它們的可靠性都不及MVP。
1.3MVP的優缺點
任何事務都存在兩面性,MVP當然也不列外,我們來看看MVP的優缺點。
優點:
-
降低耦合度,實現了Model和View真正的完全分離,可以修改View而不影響Modle
-
模塊職責劃分明顯,層次清晰
-
隱藏數據
-
Presenter可以複用,一個Presenter可以用於多個View,而不需要更改Presenter的邏輯(當然是在View的改動不影響業務邏輯的前提下)
-
利於測試驅動開發。以前的Android開發是難以進行單元測試的(雖然很多Android開發者都沒有寫過測試用例,但是隨着項目變得越來越複雜,沒有測試是很難保證軟件質量的;而且近幾年來Android上的測試框架已經有了長足的發展——開始寫測試用例吧),在使用MVP的項目中Presenter對View是通過接口進行,在對Presenter進行不依賴UI環境的單元測試的時候。可以通過Mock一個View對象,這個對象只需要實現了View的接口即可。然後依賴注入到Presenter中,單元測試的時候就可以完整的測試Presenter應用邏輯的正確性。
-
View可以進行組件化。在MVP當中,View不依賴Model。這樣就可以讓View從特定的業務場景中脫離出來,可以說View可以做到對業務完全無知。它只需要提供一系列接口提供給上層操作。這樣就可以做到高度可複用的View組件。
-
代碼靈活性
缺點:
-
Presenter中除了應用邏輯以外,還有大量的View->Model,Model->View的手動同步邏輯,造成Presenter比較笨重,維護起來會比較困難。
-
由於對視圖的渲染放在了Presenter中,所以視圖和Presenter的交互會過於頻繁。
-
如果Presenter過多地渲染了視圖,往往會使得它與特定的視圖的聯繫過於緊密。一旦視圖需要變更,那麼Presenter也需要變更了。
-
額外的代碼複雜度及學習成本。
1.4小結
在MVP模式裏通常包含4個要素:
(1) View :負責繪製UI元素、與用戶進行交互(在Android中體現爲Activity);
(2) View interface :需要View實現的接口,View通過View interface與Presenter進行交互,降低耦合,方便進行單元測試;
(3) Model :負責存儲、檢索、操縱數據(有時也實現一個Model interface用來降低耦合);
(4) Presenter :作爲View與Model交互的中間紐帶,處理與用戶交互的負責邏輯。
二.MVP在Android項目裏的基本使用
正所謂:Talk is cheap,show me the code.下面會給出示例代碼,請繼續閱讀。
我所理解到的標準MVP模式在Android應用中的實現(github鏈接)
項目的結構大致如下
項目非常簡單,只是做了一個模擬登錄的業務場景,其中,LoginRepository裏面的登錄操作替換成實際的登錄操作即可實現完整的一套業務邏輯了。
Model部分
ILoginModel
主要是定義登錄的操作接口,以及定義登錄結果的回調接口,這個接口是給Presenter用的。實際的登錄操作在LoginRepository裏面。
doLogin方法裏的部分,模擬了一個假“登錄”操作。
View部分
ILoginView
非常簡單,定義了顯示dialog、顯示登錄成功、顯示登錄失敗的幾個方法,具體的實現可以是任意一個View(視圖)、Fragment、Activity等等等等。我這裏用的Activity去實現的View接口
這裏就不用我多說什麼了吧,大家再熟悉不過的場景了。這裏面處理了實際的dialog的顯示,登錄成功的顯示和登錄失敗的顯示。最重要的是loginPresenter=new LoginPresenterImpl(this);
這裏View就持有了Presenter的引用,可以操作Presenter的相關方法了。
Presenter部分
ILoginPresenter
非常簡單,定義登錄的接口,但是這個接口是給View操作的。實際的實現在LoginPresenterImpl裏面
這裏可以看到,當我們的Presenter構造好之後,他就同時持有了View和Model的引用。View和Model層完全互不可見,但是,從這一刻開始,View卻能展示Model層擁有的數據,Model也能根據View的狀態變化或指令更新自己的數據!!!!
想象一下,如果哪一天用戶模型User去掉了name字段,要求展示nickName字段,那你需要對你的View做任何改動嗎?View是否變得可以複用了?
其實說起來有點憂傷,因爲工作環境的原因,我所闡述的MVP相關的知識都是自己在各個項目裏不斷嘗試、改良之後總結出來的,身邊也沒有大牛可以指點我,引導我更好的去認識MVP理解MVP,並且創造更好的MVP。雖然我相信這個世界上沒有最好的架構和設計模式,只有最適合的架構和設計模式,但是我更相信在適合的架構或模式裏絕對還有更好的架構和設計模式。希望大家如果在閒暇之餘看到了此文,能給我指點更好的學習路徑和方式,感激不盡。
2018年3月16日 上午11點49 於天府軟件園c11座