开发过程中相信大部分人都使用过MVC
设计模式,很多人会觉得此模式使用简单,而且在一定程度上使得代码结构清晰、易于维护。但是美中不足的是View
视图和Model
模型没有达到真正的解耦,如果代码量过大还是会造成代码的过于臃肿、可读性差,因此MVP
设计模式就逐步走入了我们的视野中被广大开发者所熟知。
MVP
是模型Model
-视图View
-主持人Presenter
的缩写,Model
负责建立数据结构和相应的业务逻辑操作;View
负责界面数据的显示更新以及用户触发的各类事件响应;Presenter
处理程序的各种逻辑分发,是模型与视图之间的桥梁。
在MVC
设计模式中View
视图是可以直接读取Model
模型中的数据的,Model
模型数据发生改变时会通知View
视图的数据显示发生相应的改变。而在MVP
设计模式中Model
模型和View
视图之间是没有直接联系的,它们是两个完全独立的模块,当Model
模型发生数据改变时,会通过Presenter
主持人通知View
视图发生相应的UI
改变。因此MVP
设计模式相对于MVC
设计模式实现了View
视图和Model
模型的完全分离,也就是说Model
模型进行业务数据处理时和View
视图数据显示没有任何关联。
代码结构图:
Model
模型
1、Model
接口
package com.wiggins.mvp.model;
import com.wiggins.mvp.listener.OnLoginListener;
/**
* @Description 登录操作接口
* @Author 一花一世界
* @Time 2017/1/17 16:17
*/
public interface LoginModel {
void login(String name, String password, OnLoginListener onLoginListener);
}
2、Model
接口实现
package com.wiggins.mvp.model.imple;
import com.wiggins.mvp.listener.OnLoginListener;
import com.wiggins.mvp.model.LoginModel;
import com.wiggins.mvp.utils.NetworkUtils;
/**
* @Description 登录操作接口实现,这里主要是业务逻辑操作
* @Author 一花一世界
* @Time 2017/1/17 16:17
*/
public class LoginModelImple implements LoginModel {
@Override
public void login(String name, String password, final OnLoginListener onLoginListener) {
if (name.isEmpty()) {
onLoginListener.onUsernameEmpty();
} else if (password.isEmpty()) {
onLoginListener.onPasswordEmpty();
} else if (!name.equals("admin")) {
onLoginListener.onUsernameError();
} else if (!password.equals("123456")) {
onLoginListener.onPasswordError();
} else if (!NetworkUtils.isNetworkAvailable()) {
onLoginListener.onFailure();
} else {
onLoginListener.onSuccess();
}
}
}
View
视图
1、View
接口
package com.wiggins.mvp.view;
import android.content.Context;
/**
* @Description 登录View接口
* @Author 一花一世界
* @Time 2017/1/17 16:18
*/
public interface LoginView {
//上下文
Context getContext();
//登录成功后跳转到首页
void moveToIndex();
//Toast提示
void showToast(String msg);
//获取界面的用户名
String getName();
//获取界面的密码
String getPassword();
}
2、View
接口实现
package com.wiggins.mvp.activity;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
import com.wiggins.mvp.R;
import com.wiggins.mvp.presenter.LoginPresenter;
import com.wiggins.mvp.presenter.imple.LoginPresenterImple;
import com.wiggins.mvp.view.LoginView;
/**
* @Description mvp登录案例
* @Author 一花一世界
* @Time 2017/1/17 17:34
*/
public class LoginActivity extends Activity implements View.OnClickListener, LoginView {
private LoginActivity mActivity = null;
private EditText mEdtName;
private EditText mEdtPwd;
private Button mBtnLogin;
private LoginPresenter loginPresenter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
mActivity = this;
loginPresenter = new LoginPresenterImple(this);
initView();
setListener();
}
private void initView() {
mEdtName = (EditText) findViewById(R.id.edt_name);
mEdtPwd = (EditText) findViewById(R.id.edt_pwd);
mBtnLogin = (Button) findViewById(R.id.btn_login);
}
private void setListener() {
mBtnLogin.setOnClickListener(this);
}
@Override
public Context getContext() {
return mActivity;
}
@Override
public void moveToIndex() {
Intent intent = new Intent(mActivity, MainActivity.class);
startActivity(intent);
finish();
}
@Override
public void showToast(String msg) {
Toast.makeText(mActivity, msg, Toast.LENGTH_LONG).show();
}
@Override
public String getName() {
return mEdtName.getText().toString().trim();
}
@Override
public String getPassword() {
return mEdtPwd.getText().toString().trim();
}
@Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.btn_login:
loginPresenter.onLoginInfo();
break;
}
}
}
Presenter
主持人
1、Presenter
接口
package com.wiggins.mvp.presenter;
/**
* @Description 登录Presenter接口
* @Author 一花一世界
* @Time 2017/1/17 17:33
*/
public interface LoginPresenter {
void onLoginInfo();
}
2、Presenter
接口实现
package com.wiggins.mvp.presenter.imple;
import com.wiggins.mvp.R;
import com.wiggins.mvp.listener.OnLoginListener;
import com.wiggins.mvp.model.LoginModel;
import com.wiggins.mvp.model.imple.LoginModelImple;
import com.wiggins.mvp.presenter.LoginPresenter;
import com.wiggins.mvp.view.LoginView;
/**
* @Description 负责完成登录界面View与Model之间的交互
* @Author 一花一世界
* @Time 2017/1/17 17:31
*/
public class LoginPresenterImple implements LoginPresenter {
private LoginModel loginModel;
private LoginView loginView;
public LoginPresenterImple(LoginView loginView) {
this.loginView = loginView;
loginModel = new LoginModelImple();
}
@Override
public void onLoginInfo() {
String name = loginView.getName();
String password = loginView.getPassword();
loginModel.login(name, password, new OnLoginListener() {
@Override
public void onUsernameEmpty() {
loginView.showToast(loginView.getContext().getResources().getString(R.string.name_not_empty));
}
@Override
public void onPasswordEmpty() {
loginView.showToast(loginView.getContext().getResources().getString(R.string.password_not_empty));
}
@Override
public void onUsernameError() {
loginView.showToast(loginView.getContext().getResources().getString(R.string.name_error));
}
@Override
public void onPasswordError() {
loginView.showToast(loginView.getContext().getResources().getString(R.string.password_error));
}
@Override
public void onSuccess() {
loginView.showToast(loginView.getContext().getResources().getString(R.string.login_successful));
loginView.moveToIndex();
}
@Override
public void onFailure() {
loginView.showToast(loginView.getContext().getResources().getString(R.string.login_failed));
}
});
}
}
回调接口
package com.wiggins.mvp.listener;
/**
* @Description 登录回调接口
* @Author 一花一世界
* @Time 2017/1/17 17:35
*/
public interface OnLoginListener {
//用户名为空
void onUsernameEmpty();
//密码为空
void onPasswordEmpty();
//用户名错误
void onUsernameError();
//密码错误
void onPasswordError();
//登录成功
void onSuccess();
//登录失败
void onFailure();
}
View
与Model
不直接进行交互,而是采用Presenter
作为View
与Model
之间交互的桥梁。其中Presenter
中同时持有View
层以及Model
层的Interface
引用,而View
层持有Presenter
层的Interface
引用。当View
层的某个界面需要展示某些数据的时候,首先会调用Presenter
层的某个接口,然后Presenter
层会调用Model
层进行数据请求,当Model
层数据加载成功之后会调用Presenter
层的回调方法通知Presenter
层数据加载完毕,最后Presenter
层再调用View
层的接口将加载后的数据展示给用户。
MVP
的优点:
- 易于维护:模块职责划分明确,层次清晰;
- 低耦合度:实现了
Model
和View
的分离; - 复用性高:一个
Presenter
可以用于多个View
; - 健壮稳定:代码容错性、可移植性强;
- 灵活性高:新增或修改需求无需更改原本的代码逻辑;
MVP
的缺点:
- 对于不是很大的项目来说会提高代码的复杂及时间成本;
View
视图的渲染放在了Presenter
中,所以View
视图和Presenter
的交互会过于频繁;- 如果
Presenter
过多地渲染了View
视图,会使得它与特定视图的联系过于紧密,一旦视图需要更改,那么Presenter
也需要更改了。
项目地址 ☞ 传送门