Android中MVC與MVP框架實例解析
首先先明確一下MVC和MVP到底是設(shè)計模式還是框架?
設(shè)計模式是從coding層面提煉出來的一種總結(jié),用來使得代碼的耦合度達到最大限度的分離,從而可以使你的代碼更好的被復(fù)用,更容易被替換,更好的擁抱需求的變化。
架構(gòu)則是著眼于更全局的高度,是保證軟件的可用性,可擴展性,可伸縮性,安全等等一系列的指標(biāo)。
設(shè)計模式服務(wù)于架構(gòu),是實現(xiàn)架構(gòu)設(shè)計的具體手段,架構(gòu)是從整體上著手設(shè)計具有宏觀性。
所以說MVC和MVP是一種框架架構(gòu)模式,不是設(shè)計模式。
一 MVC:
是一種經(jīng)典的三層架構(gòu)模式,目的是將數(shù)據(jù)層和視圖層分離,使它們的耦合度降低,符合面向?qū)ο笤O(shè)計的單一原則。

M:Model(數(shù)據(jù)層),實體模型,包含網(wǎng)絡(luò)請求、數(shù)據(jù)庫讀取、文件讀取、邏輯運算、業(yè)務(wù)bean類等等,實際中常用來對數(shù)據(jù)的封裝和保存。
V:View(視圖層),視圖界面,對應(yīng)于布局文件,更具體的就是視圖控件,例如:TextView、ImageView等等。
C:Controller(控制層),將model數(shù)據(jù)展示在view上,控制應(yīng)用程序的流程,業(yè)務(wù)邏輯的處理;對應(yīng)于工程宏的Activity、Fragment、Adapter等。
優(yōu)點:
站在Android的角度看個人認(rèn)為,MVC適用于中小型項目的開發(fā),其開發(fā)周期短,效率高,模塊職責(zé)劃分明確。
主要劃分層M,V,C三個模塊,利于代碼的維護。
缺點:雖然MVC將數(shù)據(jù)、視圖、控制器劃分三層看似層與層之間是耦合度降低了,但是實際開發(fā)中并沒有起到完全的解耦。因為View對應(yīng)的XML文件實際能做的事情很少,很多界面顯示由Controllor對應(yīng)的Activity或者Fragment給做了,導(dǎo)致View和Controller之間的分層很模糊,也就是Activity充當(dāng)Controllor也充當(dāng)了View,也就造成Activity的職責(zé)不清,導(dǎo)致Controllor層非常的臃腫,也進一步導(dǎo)致了耦合度非常的高。
二 MVP:
基于MVC衍生出來的一種模式,將MVC中的C優(yōu)化成了P,P負(fù)責(zé)業(yè)務(wù)核心邏輯,并阻斷了View和Model的直接聯(lián)系,從而使View和Model更加專注自身的邏輯。適合中大型項目。
MVP實際上是使View變薄,View和Model完全解耦。

M:和MVC中的M是一樣。
V:Activity、Fragment,View會包含一個或者多個P的引用用P的邏輯,P也可以控制V的顯示。
P(Presenter):作為V和M的橋梁,負(fù)責(zé)從M中拿數(shù)據(jù)處理后返所以每個P通常包含一個或者多個接口協(xié)議。
優(yōu)點:
View和 Model之間的耦合度降低,結(jié)構(gòu)清晰,維護方便。
便于單元測試。
代碼復(fù)用率高,擴展性強。
代碼框架更適用于快速迭代開發(fā)。
缺點:
類相對較多,如果一個Presenter被多個View引用,那么每個View中都要創(chuàng)建Presenter對象。耦合度提高,為了解決這個問題,后續(xù)引入Dagger。
三 MVP的實例(登錄實例)
下面會根據(jù)這張圖進行分析

實現(xiàn)步驟:
創(chuàng)建登錄View的接口ILoginView
創(chuàng)建登錄Model的接口ILoginModel
創(chuàng)建LoginActivity作為View的實例,實現(xiàn)ILoginView接口
創(chuàng)建LoginModel作為Model的實例,實現(xiàn)ILoginModel接口
創(chuàng)建LoginPresennter文件
效果圖
具體實現(xiàn)步驟:
1.創(chuàng)建登錄View的接口ILoginView
package showly.com.mytest.view;publicinterfaceILoginView{/** 登錄時view需要知道是否登錄成功后,更新ui* */publicvoid isLoginSuccess(boolean success);}
2.創(chuàng)建登錄Model的接口ILoginModel
package showly.com.mytest.model;publicinterfaceILoginModel{/** 需要判斷用戶輸入的賬號與密碼是否正確* */publicboolean login(String userName,String password);}
3.創(chuàng)建LoginActivity作為View的實例,實現(xiàn)ILoginView接口
publicclassLoginActivityextendsAppCompatActivityimplementsILoginView{privateEditText mEtUserName;privateEditText mMEtPsw;privateButton mLoginBtn;privateTextView mTvLoginSuccess;privateLoginPresenter mLoginPresenter;@Overrideprotectedvoid onCreate(Bundle savedInstanceState){super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);initView();initListener();}privatevoid initListener(){mLoginPresenter =newLoginPresenter();mLoginPresenter.setLoginView(this);//將View傳遞給Presenter層mLoginBtn.setOnClickListener(newView.OnClickListener(){@Overridepublicvoid onClick(View view){//根據(jù)用戶輸入的賬號 密碼進行登錄判斷String userName = mEtUserName.getText().toString();String psw = mMEtPsw.getText().toString();mLoginPresenter.login(userName, psw);}});}privatevoid initView(){mEtUserName =(EditText) findViewById(R.id.et_userName);mMEtPsw =(EditText) findViewById(R.id.et_psw);mLoginBtn =(Button) findViewById(R.id.btn_login);mTvLoginSuccess =(TextView) findViewById(R.id.tv_success);}@Overridepublicvoid isLoginSuccess(boolean success){//更新UIif(success){mTvLoginSuccess.setText("登錄成功");mTvLoginSuccess.setTextColor(Color.BLUE);}else{mTvLoginSuccess.setText("登錄失敗");mTvLoginSuccess.setTextColor(Color.RED);}}}
4.創(chuàng)建LoginModel作為Model的實例,實現(xiàn)ILoginModel接口
package showly.com.mytest.model;public class LoginModel implements ILoginModel{@Overridepublicboolean login(String userName,String password){/** 在這里可以請求服務(wù)器或者數(shù)據(jù)庫中的數(shù)據(jù)* 請求數(shù)據(jù)省略,假設(shè)請求回來的數(shù)據(jù)為name:showly password:123456* */if(userName.equals("showly")){if(password.equals("123456")){returntrue;}}returnfalse;}}
5.創(chuàng)建LoginPresennter文件
publicclassLoginPresenter{/** presenter需要持有view與model接口的引用* */privateILoginView mILoginView;privateILoginModel mILoginModel;//View層中持有presenter的引用,所以可以創(chuàng)建方法從View中傳遞過來publicvoid setLoginView(ILoginView loginView){this.mILoginView = loginView;}//Model層中不持有presenter的引用,所以LoginPresenter構(gòu)建方法中新建對象publicLoginPresenter(){//創(chuàng)建Model對象mILoginModel =newLoginModel();}//presenter持有View與Model,創(chuàng)建方法作為兩者的中間站publicvoid login(String name,String psw){//將用戶輸入的用戶名 密碼傳遞到Model層進行判斷boolean isSuccess = mILoginModel.login(name, psw);//將Model層對比數(shù)據(jù)后的結(jié)果返回mILoginView.isLoginSuccess(isSuccess);}}
6.效果圖

四 開閉原則介紹
在這里介紹一下開閉原則,我們在MVP的實例中看到,我們的View和Model是用接口的形式與Presenter進行關(guān)聯(lián),有沒想過這是為什么呢?
這就涉及到開閉原則了,也就是對擴展進行開放,對修改進行關(guān)閉。
舉個例子:
我們定義一個接口:
publicinterfaceILoginModel{publicLoginBean getLoginBean();}
然后創(chuàng)建LoginModel實現(xiàn)這個接口:
publicclassLoginModelimplementsILoginModel{publicLoginBean getLoginBean(){//假設(shè)這里用原生系統(tǒng)SQLiteOpenHelper//查詢數(shù)據(jù)庫的代碼}}
此時項目經(jīng)理說用原生的數(shù)據(jù)庫查詢不好,需要換GreenDao進行查詢,這個時候我們就需要對getLoginBean方法進行修改,而項目中其它很多地方已經(jīng)使用了該方法,那我們要怎樣才能降低修改的成本呢?
其實我們剛開始設(shè)計的時候,就應(yīng)該考慮到這個問題,根據(jù)開閉原則,我們可以讓其它地方使用以下的方法進行調(diào)用:
publicvoid getData(ILoginModel loginModel){loginModel.getLoginBean();}
這樣需要修改的時候,我們可以對修改進行關(guān)閉,也就是不修改LoginModel,而是新創(chuàng)建GreenDaoModel,這樣就能降低修改的成本。
需要源碼的童鞋在公眾號【龍旋】對話框輸入【MVPDemo】即可獲取哦.
