MVP的使用在Android中已经不是什么新鲜事了。这段时间,自身开发经验有所积累,加上部门最近关于“MVP架构模式”的讨论,我对MVP设计模式有了更深刻的理解。整个文章几乎重写了三遍,希望能和大家互相学习一下。
重要的事情说在前面,这里针对一个“足够小”的业务模块进行讨论。至于多模块,先要关注的应该是“通信模型”,这个我们放到以后写“组件化与模块化”再说。
对了,本文面向小白,重点移步第3小节。
1. 什么是设计模式
在我们还是android小白的时候(虽然有的顽固派现在也这么干),我们常把各种逻辑都塞在Activity中。比如:更新界面,启动计算线程,网络请求,数据处理... 最后干出几千行的类!别说维护了,就是走一遍逻辑也需要好长时间。
在codeReview时,面对这样的代码,程序员们是思想是紊乱的,表情是凝固的,内心是奔溃的。
为了减轻某些类的负担(也是程序员们的负担),也为了使代码逻辑清晰,程序员们规定了一系列的设计模式。项目成员必须严格遵守设计模式,代码“传宗接代”时才能被后辈读懂。在这些设计模式中,就包含MVP与MVC设计模式。
先来帮大家回顾一下MVC。我相信,在Android开发之前,大家或多或少都接触过MVC设计模式。简单说一下,Model(模型)负责在数据库中存取数据;View(视图)用于处理数据显示的部分;Controller(控制器)是模型调度站,当请求传递给控制器,控制器就会指明哪个模块取数据,哪个视图去显示。
他的模型如下:

由设计模式图可以看出来,MVC中view会拿Model去使用的。也就是说:在view层,我们可以去调度Model。也就是说,我们的Activity可以直接进行调度网络请求模块。
显然,这种模式下view的职责还不是很“干净”。为了让View单纯的只做界面修改。程序员改进了MVC模式,将View的繁杂事务全部托付给Presenter 处理,形成了MVP。
MVP设计模式的概念
MVP 全称为Model-View-Presenter,是基于MVC演化过来的。
为了使View层剪掉更多的包袱,我们将Model中分离出来,创建一个Presenter让他作为Model和View的中间传递者。

MVP模式中,把UI显示逻辑抽象成View接口,把业务逻辑抽象成Presenter接口,Model还是用于查询的。
这样做的好处是什么?
1.分离出Activity视图逻辑和业务逻辑,降低了耦合度;
2.把业务逻辑扔给Presenter,view只负责修改界面,提高代码的可阅读性;
3.Presenter被抽象成接口,可以有多种具体的实现,所以方便进行单元测试;
4.把业务逻辑抽到Presenter中去,避免后台线程引用着Activity导致Activity的资源无法被系统回收从而引起内存泄露和内存溢出(OOM);
当然凡事没有绝对的,这也带来了一系列新的问题,我们下文再说。
MVP的使用
(下面是建在模块足够小的情况下使用,如果有许多模块,先要考虑边界问题)
我们再次来看一下三个层次的职责:
Model层: 业务逻辑与实体模型。
Presenter: 从Model中获取数据,并将其返回给View。
View: 负责渲染UI、接收用户输入 并转发给Presenter。
再次强调一下,Model与javaBean 两者并不是一个东西。首先Model的概念是出现在设计模式中,有MVP或者MVC才会有Model,可以包含一定的逻辑。而javabean是一种遵循一种规范写出来的java类。对于某些业务简单的model,可以是一个bean。而bean类绝不等于Model。
首先我给大家上个MVP的目录结构:

- Contract类
嗯,估计有朋友就会问了,为啥多出来了个Contract(契约)?
在谷歌官方MVP的demo中还有一个比较有意思的就是Contract了,这个合同类的接口把P和V的所有方法全部写在一起。类似一个功能目录,它约定了P与V提供的接口。
因为MVP使用起来,可能代码比较杂乱。通过契约类,我们就可以很方便的看出/规划出 该模块主要实现的功能。
强调 : Contract中定义的是View 和 Presenter接口,不包括Model。
public class LoginContract {
interface ILoginPresenter {
//校验密码
void reqLogin(int uid, String password);
//释放资源
void release();
}
interface ILoginView {
//成功界面
void showSuccess();
//失败界面
void showFailure();
}
}
2.View类
veiw界面,负责界面展示(这里我们用Activity举例)
它应该实现Contract.view接口
View层不应该存储任何业务状态:它应该是单纯的执行界面显示
View层不应该有业务逻辑:全部抛给P层实现
View职责:展示业务领域数据,以及接受用户输入,分发给Presenter
View 不能直接引用Model
class LoginActivity implements LoginContract.ILoginView {
private LoginContract.ILoginPresenter mLoginPresenter;//需要持有P层
void onCreate(Bundle savedInstanceState) {
mLoginPresenter=new LoginPresenter(this);//实例化p层
}
//成功界面
void showSuccess(){
//do something
}
//失败界面
void showFailure(){
//do something
}
//释放p层资源
void release(){
}
}
3.Presenter类
Presenter:不应该存在大量的业务逻辑(业务请求尽量交给Model处理:如网络请求)。
数据完整性,合法性的校验也应该交给Model层处理。
presenter尽量为一个view服务,尽量不要与别的p互相持有。
class LoginPresenter implements LoginContract.ILoginPresenter {
private LoginContract.ILoginView mLoginView;
public LoginPresenter( LoginContract.ILoginView view){
this.mLoinView = view;
}
//校验密码
void reqLogin(int uid, String password){
//调用model网络请求让服务器进行对比,并分析返回结果
//调用mLoingView方法实现界面修改
}
//释放资源
void release(){
}
}
注:View 和 Presenter是紧密联系的,不应该存在单独使用View或者单独使用Presenter的情况。
4.model类
Model是业务逻辑的主体
Model不是一个类,而是一个角色,可以由一系列类来承担,最终实现某个可以独立出来的功能。
Model可以设计为持有业务状态。
CRUD操作、网络请求、数据持久化均在Model层进行。
一个Model可以被多个Presenter调用。
关于一些细节
1.开发过程中,很多时候是控件担任着view的责任。Activity多数情况下是粘合剂的作用。
2.MVP的缺点:视图与逻辑本来就是一个相辅相成的事。MVP的分层势必会导致view和Presenter的交互会过于频繁,嵌套紧密。所以在修改界面的时候,通常P也需要一定的改动。
3.设计模式是为了让条理清晰,它的代价就是需要写很多“多余的”代码。这就鱼与熊掌的问题吧,所以具体设计中,需要做以权衡。
好了,今天聊的就是这么多。如果小伙伴发现什么问题或者疑惑,欢迎留言。
你的意见就是我的进步。
网友评论