Android中MVC与MVP框架实例解析
首先先明确一下MVC和MVP到底是设计模式还是框架?
设计模式是从coding层面提炼出来的一种总结,用来使得代码的耦合度达到最大限度的分离,从而可以使你的代码更好的被复用,更容易被替换,更好的拥抱需求的变化。
架构则是着眼于更全局的高度,是保证软件的可用性,可扩展性,可伸缩性,安全等等一系列的指标。
设计模式服务于架构,是实现架构设计的具体手段,架构是从整体上着手设计具有宏观性。
所以说MVC和MVP是一种框架架构模式,不是设计模式。
一 MVC:
是一种经典的三层架构模式,目的是将数据层和视图层分离,使它们的耦合度降低,符合面向对象设计的单一原则。

M:Model(数据层),实体模型,包含网络请求、数据库读取、文件读取、逻辑运算、业务bean类等等,实际中常用来对数据的封装和保存。
V:View(视图层),视图界面,对应于布局文件,更具体的就是视图控件,例如:TextView、ImageView等等。
C:Controller(控制层),将model数据展示在view上,控制应用程序的流程,业务逻辑的处理;对应于工程宏的Activity、Fragment、Adapter等。
优点:
站在Android的角度看个人认为,MVC适用于中小型项目的开发,其开发周期短,效率高,模块职责划分明确。
主要划分层M,V,C三个模块,利于代码的维护。
缺点:虽然MVC将数据、视图、控制器划分三层看似层与层之间是耦合度降低了,但是实际开发中并没有起到完全的解耦。因为View对应的XML文件实际能做的事情很少,很多界面显示由Controllor对应的Activity或者Fragment给做了,导致View和Controller之间的分层很模糊,也就是Activity充当Controllor也充当了View,也就造成Activity的职责不清,导致Controllor层非常的臃肿,也进一步导致了耦合度非常的高。
二 MVP:
基于MVC衍生出来的一种模式,将MVC中的C优化成了P,P负责业务核心逻辑,并阻断了View和Model的直接联系,从而使View和Model更加专注自身的逻辑。适合中大型项目。
MVP实际上是使View变薄,View和Model完全解耦。

M:和MVC中的M是一样。
V:Activity、Fragment,View会包含一个或者多个P的引用用P的逻辑,P也可以控制V的显示。
P(Presenter):作为V和M的桥梁,负责从M中拿数据处理后返所以每个P通常包含一个或者多个接口协议。
优点:
View和 Model之间的耦合度降低,结构清晰,维护方便。
便于单元测试。
代码复用率高,扩展性强。
代码框架更适用于快速迭代开发。
缺点:
类相对较多,如果一个Presenter被多个View引用,那么每个View中都要创建Presenter对象。耦合度提高,为了解决这个问题,后续引入Dagger。
三 MVP的实例(登录实例)
下面会根据这张图进行分析

实现步骤:
创建登录View的接口ILoginView
创建登录Model的接口ILoginModel
创建LoginActivity作为View的实例,实现ILoginView接口
创建LoginModel作为Model的实例,实现ILoginModel接口
创建LoginPresennter文件
效果图
具体实现步骤:
1.创建登录View的接口ILoginView
package showly.com.mytest.view;publicinterfaceILoginView{/** 登录时view需要知道是否登录成功后,更新ui* */publicvoid isLoginSuccess(boolean success);}
2.创建登录Model的接口ILoginModel
package showly.com.mytest.model;publicinterfaceILoginModel{/** 需要判断用户输入的账号与密码是否正确* */publicboolean login(String userName,String password);}
3.创建LoginActivity作为View的实例,实现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){//根据用户输入的账号 密码进行登录判断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.创建LoginModel作为Model的实例,实现ILoginModel接口
package showly.com.mytest.model;public class LoginModel implements ILoginModel{@Overridepublicboolean login(String userName,String password){/** 在这里可以请求服务器或者数据库中的数据* 请求数据省略,假设请求回来的数据为name:showly password:123456* */if(userName.equals("showly")){if(password.equals("123456")){returntrue;}}returnfalse;}}
5.创建LoginPresennter文件
publicclassLoginPresenter{/** presenter需要持有view与model接口的引用* */privateILoginView mILoginView;privateILoginModel mILoginModel;//View层中持有presenter的引用,所以可以创建方法从View中传递过来publicvoid setLoginView(ILoginView loginView){this.mILoginView = loginView;}//Model层中不持有presenter的引用,所以LoginPresenter构建方法中新建对象publicLoginPresenter(){//创建Model对象mILoginModel =newLoginModel();}//presenter持有View与Model,创建方法作为两者的中间站publicvoid login(String name,String psw){//将用户输入的用户名 密码传递到Model层进行判断boolean isSuccess = mILoginModel.login(name, psw);//将Model层对比数据后的结果返回mILoginView.isLoginSuccess(isSuccess);}}
6.效果图

四 开闭原则介绍
在这里介绍一下开闭原则,我们在MVP的实例中看到,我们的View和Model是用接口的形式与Presenter进行关联,有没想过这是为什么呢?
这就涉及到开闭原则了,也就是对扩展进行开放,对修改进行关闭。
举个例子:
我们定义一个接口:
publicinterfaceILoginModel{publicLoginBean getLoginBean();}
然后创建LoginModel实现这个接口:
publicclassLoginModelimplementsILoginModel{publicLoginBean getLoginBean(){//假设这里用原生系统SQLiteOpenHelper//查询数据库的代码}}
此时项目经理说用原生的数据库查询不好,需要换GreenDao进行查询,这个时候我们就需要对getLoginBean方法进行修改,而项目中其它很多地方已经使用了该方法,那我们要怎样才能降低修改的成本呢?
其实我们刚开始设计的时候,就应该考虑到这个问题,根据开闭原则,我们可以让其它地方使用以下的方法进行调用:
publicvoid getData(ILoginModel loginModel){loginModel.getLoginBean();}
这样需要修改的时候,我们可以对修改进行关闭,也就是不修改LoginModel,而是新创建GreenDaoModel,这样就能降低修改的成本。
需要源码的童鞋在公众号【龙旋】对话框输入【MVPDemo】即可获取哦.
