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;
@Override
protectedvoid 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(){
@Override
publicvoid 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);
}
@Override
publicvoid isLoginSuccess(boolean success){
//更新UI
if(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{
@Override
publicboolean 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】即可获取哦.