MVP 模式接触
以前查看别人的时候会看不懂别人的项目结构,里面有个 presenter,其实代码勉强看得懂,但是逻辑会感觉很混乱,所以后来学习了下 MVP 模式,现在需要整理一下。
MVC 和 MVP 的区别
说起 MVP 模式不得不提起 MVC 模式,也是我们接触最多,也应该是最早接触的,虽然一开始的时候感觉自己写的连 MVC 模式都谈不上😁。
MVC 模式的全程是 Model-View-Controller
- Model 业务逻辑和实体对象
- View 布局文件
- Controller 控制器,也就是常说的 Activity
MVC 模式在小型的项目上面很适用,说白了就是简单粗暴,逻辑简单,View 就是布局,实际的关于数据绑定的工作都是交给 Activity 来完成,这样子搞得 Activity 又像 Controller 又像是 View,借用网上的图来表示这种关系
image.png
也就是说由于 Activity 的职责很模糊,就会导致 Data 和 View 的关系很乱。
而如果换做是 MVP 模式的话,关系则就会清晰很多
image.png
其中 MVP 代表的是 Model-View-Presenter,其中最大的特点就是 Presenter,它是 Model 和 View 之间的桥梁,它们分别的作用如下所示:
- View 对应着Activity,对应着 View 的绘制和用户之间的 ui 交互
- Model 和在 MVC 里面的作用一样是业务逻辑和实体对象
- Presenter 负责完成 View 和 Model 之间的交互。
和上面的图描述的一样,接下来的一张图则更加明了,是一张很经典的描述两种模式区别的图
image.png
总结
MVP 减少了 Activity 要做的事情,简化了它的代码,把许多事情放到 Presenter 里面去完成,模块划分清楚,层次清楚,从而降低了耦合(这里的低耦合,我通俗的理解就是在 MVP 模式里面,View 和 Presenter 之间,Presenter 和 Model之间可能互相都持有引用(这里错了,是只能 Presenter 持有 Model),但是 View 不会持有 Presenter 的引用,反之也没有)。
例子分析
这里我们拿一个登录的例子来说这个问题,首先我们先看下这个项目架构,是不是感觉分的很清晰,Model 层,Presenter 层和 View 层都分的很清楚。
image.pngModel 层实现
userBean 类
public class UserBean {
private String userName;
private String password;
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return "User(" + "\n" + "userName = " + userName + "\n" + "password = " + password + ")";
}
}
定义的 LoginModel 接口规定 Model 类要实现的功能,当然是登录,但是这里要注意的是这里面使用的 OnLoginFinishListener 这个接口是 Presenter 里面的,通过它,Presenter 才能知道是否登录成功。
public interface LoginModel {
void login(UserBean userBean, OnLoginFinishListener onLoginFinishListener);
}
接着是 LoginModelImpl 这个 LoginModel 的实现类
public class LoginModelImpl implements LoginModel {
@Override
public void login(UserBean userBean, final OnLoginFinishListener onLoginFinishListener) {
final String userName = userBean.getUserName();
final String password = userBean.getPassword();
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
boolean error = false;
if (userName == null || userName.isEmpty()) {
onLoginFinishListener.onUserError();
error = true;
}
if (password == null || password.isEmpty()) {
onLoginFinishListener.onPasswordError();
error = true;
}
if (!error) {
onLoginFinishListener.onSuccess();
}
}
}, 2000);
}
}
View 类的实现
LoginView 接口,定义 Acitivity 要完成哪些交互。
public interface LoginView {
void setUserError();
void setPasswordError();
void onSuccess();
}
接着是这个接口的实现类,在这里面实例化一个 Presenter 并持有它,通过 presenter 的实例对象来完成具体的登录和 onDestroy 里面的释放操作,释放掉对 view 的持有,也就是 activity 的持有(这里涉及内存泄漏的知识)。同时也要实现一些 ui 的交互,也就是 LoginView 接口里面定义的方法。
public class LoginActivity extends AppCompatActivity implements View.OnClickListener, LoginView {
private EditText userEditText;
private EditText passwordEditText;
private Button loginButton;
private LoginPresenter loginPresenter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
userEditText = (EditText)findViewById(R.id.et_user_name);
passwordEditText = (EditText)findViewById(R.id.et_password);
loginButton = (Button)findViewById(R.id.btn_login);
loginButton.setOnClickListener(this);
loginPresenter = new LoginPresenterImpl(this);
}
@Override
protected void onDestroy() {
loginPresenter.onDestroy();
super.onDestroy();
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_login:
UserBean userBean = new UserBean();
userBean.setUserName(userEditText.getText().toString());
userBean.setPassword(passwordEditText.getText().toString());
loginPresenter.validateUser(userBean);
break;
}
}
@Override
public void setUserError() {
Toast.makeText(this, "用户名错误", Toast.LENGTH_SHORT).show();
}
@Override
public void setPasswordError() {
Toast.makeText(this, "密码错误", Toast.LENGTH_SHORT).show();
}
@Override
public void onSuccess() {
Toast.makeText(this, "登录成功", Toast.LENGTH_SHORT).show();
}
}
最重要的 Presenter 层
LoginPresenter 类,只用实现两个方法,一个是登录,一个是销毁。
public interface LoginPresenter {
void validateUser(UserBean userBean);
void onDestroy();
}
之前提到的 OnLoginFinishListener
public interface OnLoginFinishListener {
void onUserError();
void onPasswordError();
void onSuccess();
}
LoginPresenter 的实现类,这里在登录方法里面调用 Model 的登录,并且在登录的回调里面调用 LoginView 的方法,通过这么做来链接 Model 层和 View 层,由此可见对于 MVP 模式而言,最重要的是 Presenter 层,View 层和 Model 层只要管好自己的事,Model 层管好业务逻辑,View 层管好 ui 交互,而对于 presenter 来说最重要的就是处理两者之间的关系,作为一个协调者。