一个好的好的设计模式可以帮我们很好的管理我们的代码,也会方便于我们的后期的扩展。特别是针对我们这样的新手,学好一个好的代码管理是很有必要的。所以今天,和大家一起学习一下MVP模式。
一、MVP设计模式概述
什么是MVP设计模式
MVP,分别是Model-View-Presenter,即模型-视图-提出者。
Model:模型,实现业务逻辑和实例生成。
View:视图,对应的界面布局,以及界面布局的方法。
Presenter:一个活动中,主要的业务交互,作为view和model的中间传达者。
什么是MVC设计模式
MVP,分别是Model-View-Presenter,即模型-视图-控制器。
Model:模型,实现业务逻辑和实例生成。
View:视图,对应的界面布局,以及界面布局的方法。
Controllor:作为页面的控制器,响应view的交互,对应于Android中的Activity
对比
这么说可能不是很清晰,我们用两张图片来描述一下。
但是在Android我们的可以知道,其实Avtivity不仅要处理view的呈现,还要负责处理页面的业务逻辑,显得Activity不仅像view,又像controllor的结合体,导致Acitivity整体臃肿,超过1000行都是常事。
所以,为了更好的解耦这样的情况,让Activity更好的只关心页面的呈现,将主要的业务交互放置在presenter中实现,让Presenter作为view与model的中间传递者。减少Activity的体积。降低耦合度。
在MVP模式中,我们要做到,view层和model不能直接通信,要想通信,必须通过Persenter这一中间件。并且,view,model,presenter都是接口,之间的通讯也都是通过接口实现的。
二、 MVP的简单实现
接下来我们通过一个基础的实例,让我们来实现一个简单的MVP模式的登录界面。从实例的角度,我们来分析一个具体的MVP模式。看看他是怎样实现解耦,以及明显的内容划分。
Model层:
model层是用来实现某一层里面的业务逻辑。
那么,在一个登录界面中,我的model层就是实现登录密码检验的功能。
首先,我们需要有一个对象类,来表达我们需要检验的对象。
public class User {
private String name;
private String password;
public User(String name,String password){
this.name = name;
this.password = password;
}
...省略getter和setter
}
然后我们需要一个LoginModel的接口函数,来申明我们的登录所需要的函数。
public interface LoginModel {
void login(User user);
}
其次是model的实现类,实现我们刚才在LoginModel中申明的login函数。
这里我们延时2秒钟,用于模拟登陆效果。
public class LoginModelImpl implements LoginModel{
@Override
public void login(User user) {
final String name = user.getName();
final String password = user.getPassword();
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
Boolean error =false;
if (TextUtils.isEmpty(name)){
error = true;
//输入错误
System.out.println("输入错误");
}
if (TextUtils.isEmpty(password)){
error = true;
//输入错误
System.out.println("输入错误");
}
if(!error){
//登录成功
System.out.println("登录成功");
}
}
},2000);
}
}
View层:
对应的界面布局,以及界面布局的方法。
这里我们需要实现的waitDialog的显示与消失,以及登录成功和失败的提醒,所以建立我们的view接口类,LoginView.
public interface LoginView {
void loginSuccess();
void ErrorPass();
void ErrorEnter();
void showDialog();
void hideDialog();
}
然后我们的实例就是Activity,使Acitivity继承我们的接口,实现我们接口中的方法。
//申明persenter实例
LoginPresenter presenter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
ButterKnife.bind(this);
btn.setOnClickListener(this);
//通过Presenter实例类申明presenter
presenter = new LoginPresenterImpl(this);
}
@Override
public void loginSuccess() {
Toast.makeText(this,"登录成功",Toast.LENGTH_SHORT).show();
}
@Override
public void ErrorPass() {
Toast.makeText(this,"密码错误",Toast.LENGTH_SHORT).show();
}
@Override
public void ErrorEnter() {
Toast.makeText(this,"输入错误",Toast.LENGTH_SHORT).show();
}
@Override
public void showDialog() {
loginProgress.setVisibility(View.VISIBLE);
}
@Override
public void hideDialog() {
loginProgress.setVisibility(View.GONE);
}
@Override
public void onClick(View v) {
User user = new User(name.getText().toString(), password.getText().toString());
//将检验事件回调给presenter,让他通知model进行检验
presenter.checkLogin(user);
}
}
Presenter层:
接下来是重点了,我们的Presenter类。
传递
作为view和model的中间传达者。view告诉presenter需要检验了,然后presenter在告诉model,并把得到的user值给model,让model进行检验。
申明presenter的接口类。
public interface LoginPresenter {
void checkLogin(User user);
}
接口实现类LoginPresenterImpl,我们在presenter的实例中,获取到view和model的实例,然后在里面对其进行信息传递。
public class LoginPresenterImpl implements LoginPresenter,LoginModelListener{
LoginView view;
LoginModel model;
public LoginPresenterImpl(LoginView loginView){
//从Activity中获取到对view的引用
this.view = loginView;
//申明一个新的model
model = new LoginModelImpl();
}
@Override
public void checkLogin(User user) {
//保证视图是存在的
if (view == null){
return;
}
//显示view中的dialog,然后对user信息进行验证
view.showDialog();
model.login(user,this);
}
}
这样我们就实现了将view的事件,通过presenter传递给了model层,让他去处理这一次的检验事件,但是大家有没有发现,我们是将这个事件传递给了model,但是model在处理完了之后,他又怎么将得到的结果通知给presenter呢?因为我们要避免view和model直接接触。所以在这里,我们还需要一个listenter的接口类,让presenter继承这个接口,在传递给model的时候,携带接口一起传过去。然后model通过这个接口告诉presenter,presenter再去通知view。
public interface LoginModelListener {
void loginSuccess();
void loginError();
}
public interface LoginModel {
void login(User user, LoginModelListener listener);
}
public class LoginModelImpl implements LoginModel{
@Override
public void login(User user, final LoginModelListener listener) {
final String name = user.getName();
final String password = user.getPassword();
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
Boolean error =false;
if (TextUtils.isEmpty(name)){
error = true;
//输入错误
System.out.println("输入错误");
listener.loginError();
}
if (TextUtils.isEmpty(password)){
error = true;
//输入错误
System.out.println("输入错误");
listener.loginError();
}
if(!error){
//登录成功
System.out.println("登录成功");
listener.loginSuccess();
}
}
},2000);
}
}
至此,我们就实现了一个简单的MVP的demo,是不是觉得Acitivity里面的逻辑很简单了,看着一下字就轻松了很多。
三、总结
那么我们现在结合刚才的案例。我们再来总结一下。
因为我们要保证view和model直接不能直接通信,所有之间的交互,我们都要通过presenter这一个中间类来进行传递。persenter中拥有view和model的引用,然后,view中有事件产生时,将事件以及需要的参数传给presenter,presenter再交给model,待model处理完了之后,将反馈信息传给presenter,presenter再将返回结果告诉view,最后呈现给用户。