浅析MVP

因为红岩网校布置作业的原因,在这个时候才算是比较正式的接触了这个模式,本着学习了还是要记笔记的原则,还是稍微写一下吧…
ps.可能里面的一些概述有问题,可能还是因为本人太新手了吧233,而且逻辑可能有点乱otz

MVP是指Model-View-Presenter,对应关系如下

类型 解释
Model 模型
View 视图
Presenter 表示器

而在介绍这个之前我还是想提及一下它的发展元祖——MVC

MVC模式

MVC是指Model-View-Controller
对应关系如下

类型 解释
Model 模型
View 视图
Controller 控制器

而MVC的三个部分的通信关系可以如下图所示
MVC通信模式

在实际开发的过程之中,View就可以对应每个layout的xml文件,Model就是每个javaBean类,而Activity原先是想设定成Controller的处理层。好比来说

1
2
3
4
5
6
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// the thing to do...
}
});

onClick内的代码块就可以理解成为是一个Controller内的处理业务逻辑的语句。而本身Activity内的工作只是需要绑定layout文件里面的每个控件的id,然后通过用户的行为来处理事件就好了。
但是随着发展,这样的操作远远不能达到我们的需要,譬如说要动态显示或者隐藏一个控件,就必须在Activity里面进行相应的业务操作,这样就导致了Activity同时是View层和Controller层的载体,导致整个Activity的代码臃肿,一个不小心就几百行甚至几千行。
而这样的代码臃肿就导致了阅读代码的时候难以理解,同时耦合度也非常的高。
这个时候就可以发现我们定义的Controller层貌似已经没有什么作用了,显然是ModelView层直接通信。就可以直接理解成MV(C)这个样子了。

因此,MVP与MVVM应需求而生。

MVP

在正式的开发过程之中,MVP的模式,M层,P层,V层之间的通信以及业务处理范围也不尽相同,在这里只是很笼统的讲公认的和一些自己的理解。

通信方式

首先还是从通信方式开始,见图:

MVP通信方式

从这个图显然的可以看出,Model层没有与View层进行直接的通信了,都是通过的Presenter层进行的通信。
那么,这些层直接的通信是怎么实现的呢?
是通过每个层原先定义的接口,再具体的实现类里面在写清楚业务逻辑,以此达到解耦的目的。

在之后,还是以一个简单的登陆界面作为例子,很简单的讲述一下一个mvp模式的实现。

Model(模型层)

首先可以通过接口来定义该模型需要的功能有哪些

1
2
3
4
5
6
public interface IUser {
String getName();
String getPassword();
String getNickName();
void checkInfo(String stuNum,String idNum,CheckListener listener);
}

在这里就分别定义了获得账号、密码、昵称,以及检查登陆信息的方法。
其中的listener就是给p层的一个回调,就是成功与失败两种情况,这个就不贴代码上来了。(主要是自己并不喜欢贴很多代码上来,感觉没有什么可读性,搞的让人很没有耐心看完)

而之后就是一个具体的实现类,在这里我们命名为UserModel,实现IUser的接口之后复写相关的方法,同时声明本身的自带属性。它就是相当于一个JavaBean类的一个集成版,里面不仅仅含有一些信息,还会有一些相关于这些数据的处理。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class UserModel implements IUser {

private String name;
private String password;
private String nickName;

@Override
public String getName() {
return name;
}

@Override
public String getPassword() {
return password;
}

@Override
public String getNickName() {
return nickname;
}

@Override
public void checkInfo(String stuNum,String idNum,CheckListener listener) {
// todo:...
}

}

构造器的有无以及传入内容凭借实际的开发需求决定。譬如说你的数据本身就是通过之后的方法进行网络请求之后才获得的,那就没有必要声明一个构造器了,在网络请求之后把相应的数据放入就好。

View(视图层)

v层与mvc之中的v层最大的不同的地方就是mvp的v层还是通过接口来定义了v层的业务逻辑,譬如如下的IView

1
2
3
4
public interface ILogInView {
void logInResult(Boolean succeed,String info);
void clean();
}

而v层在什么时候实现呢?是在直接完成Activity部分的代码的时候在里面时候v层的接口。

Presenter(表达器)

p层作为mvp模式之中最为重要的一个部分,它承担的是所有的业务处理,同时也是m层与v层沟通的桥梁。而它的实现与m层相似,首先都是要有一个接口来定义它相应的业务逻辑的主要内容

1
2
3
4
public interface ILogInPresenter {
void doLogIn(String id,String pw);
void finish();
}

这里因为只有登录验证和验证完成后的两个逻辑,然后接口就显得比较简单了。
而p层定义的接口必然是需要有一个实体类来实现的,在这里我们命名为presenterCompl,然后就可以利用这个接口实现业务逻辑。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public class LogInPresenterCompl implements ILogInPresenter {

private ILogInView logInView;
private UserModel userModel;


public LogInPresenterCompl(ILogInView iLogInView){
this.logInView = iLogInView;

}

@Override
public void doLogIn(String id, String pw) {
userModel = new UserModel();
userModel.checkInfo(id,pw,new CheckListener() {
@Override
public void onSucceed(String info) {
logInView.logInResult(true,info);
}

@Override
public void onFail(String info) {
logInView.logInResult(false,info);
}
});
}

@Override
public void finish() {
logInView.clean();
}
}

在其中我们可以看到,PresenterCompl有了m层的实体类和一个v层的实体类,通过p层的具体逻辑分别调用m层与v层的逻辑。当然重要的是,PresenterCompl在最开始构造的时候就需要传入一个view的示例

在Activity内的具体实现

可能讲了这么多还有点懵逼。
但是可能看了具体实现的Activity会稍微好一点。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
public class LogInActivity extends AppCompatActivity implements ILogInView,View.OnClickListener {

private String TAG = "LogInActivity";
private LogInPresenterCompl presenterCompl;
private EditText id;
private EditText pw;
private Button logIn;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);

presenterCompl = new LogInPresenterCompl(this);

id = findViewById(R.id.account);
pw = findViewById(R.id.pw);
logIn = findViewById(R.id.login);

logIn.setOnClickListener(this);
}

@Override
public void logInResult(Boolean succeed, String info) {
// todo:...
}

@Override
public void clean() {
// todo:...
}

@Override
public void onClick(View v) {
switch (v.getId()){
case R.id.login:
String name = id.getText().toString();
String password = pw.getText().toString();
presenterCompl.doLogIn(name,password);
break;
}
}
}

我们可以看见其实是Activity实现了v层的接口,而构造p层实体类的时候引用的v层就是这个Activity。而在里面我们可以看见p层实体类的使用还是在点击事件之后的。
这样一个很简单的MVP结构模式就出来了。

优点&缺点

优点
  • 达到MVC所没有的解耦
  • 让整个流程变得更加清晰
  • 可以利用不同的p层实体类来进行单元测试
  • 能够更加轻松的看代码了
缺点
  • 接口数量变多
  • m,p层逻辑处理容易交叉并混淆
  • 代码量增加

关于m层以及p层的处理范围问题

啊…怎么说呢,有的东西的产生就是有利也有弊的吧。不然为什么会让我们这么纠结呢2333

就MVP架构中的p层的处理范围就一直是不定的。在网上很多的都是偏重于p层仅仅只是java代码,用来处理相应的逻辑,而对应的Android控件内容的处理,都交给了m层进行处理。
而在Google的示例代码之中我们可以看见,它其实是弱化了m层来处理逻辑的能力的,相应的,将原本放在m层处理的逻辑,统一都放在了p层之中。

而自己最开始看见了m,p,v相应的解释之后其实自己也思考了很久,这个到底怎么写,怎么用。
最开始一直是想把所有的有关逻辑的操作全部放在p层,而m层只需要达成数据的读取操作就好了。这样的好处是,减少了m层与p层的逻辑通信,可以少添加回调。这样写着比较爽快。同时这个想法也契合了Google的一些理念。
但是之后又转念一想,mvp的出现是为了解耦的,假如说大部分的m层的逻辑都放在p层,那必然会导致有些时候p层成了一个独立的新的p类,如果要根据其他的业务逻辑来创建一个p类,那必然会造成p类无法很好地得到复用,同时原本放在m层处理的代码都放到了p层,在相同的地方需要m层对应的相同逻辑,导致了代码重复量也会增加。
emmm所以自己纠结万分了之后最终还是采用了m层与p层都有一定逻辑的处理方式(回调多就多吧反正mvp里面的接口也不差这几个)

各位也不要被我的话给听信了,写代码就是怎么爽怎么写(~ ̄▽ ̄)~