MVP模式理解与使用

目录

1.为什么使用MVP模式

1.1实例说明

2.如何使用MVP模式

2.1MVP实现之拆分

2.2MVP实现之接口通信

2.3MVP实现之功能仓库

3.MVP模式的优缺点


1.为什么使用MVP模式

在Android开发中,Activity的首要职责是加载应用的布局和初始化用户界面,接受和处理来自用户的操作请求。但是随着界面和逻辑的复杂度不断的提升,Activity类的职责不断增强而变得庞大臃肿。那么我们就需要通过MVP模式解决混乱、冗余、耦合重的问题。

扫描二维码关注公众号,回复: 14996943 查看本文章

1.1实例说明

下面是一个用户登录的Demo

public class LoginActivity extends AppCompatActivity {
    EditText inputUserName;
    EditText inputPassword;
    Button btnLogin;
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ...
        //点击登录
        btnLogin.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //获取账户和密码
                final String userName = inputUserName.getText().toString();
                final String password = inputPassword.getText().toString();
                //判空
                boolean isEmptyPassword = userName == null || userName.length() == 0;
                //是否符合规范
                boolean isUserNameValid = Pattern.compile("^[A-Za-z0-9]{3,20}+$").matcher(userName).matches();
                boolean isPasswordValid = Pattern.compile("^[A-Za-z0-9]{3,20}+$").matcher(password).matches();
                
                if (isEmptyPassword) {
                    Toast.makeText(LoginActivity.this, "请输入帐号密码", Toast.LENGTH_SHORT).show();
                } else {
                    if (isUserNameValid && isPasswordValid) {
                        new Thread(new Runnable() {
                            @Override
                            public void run() {
                                //..登录请求
                                boolean loginResult = false;
                                //登录结果
                                if (loginResult) {
                                    Toast.makeText(LoginActivity.this, "登录成功", Toast.LENGTH_SHORT).show();
                                } else {
                                    Toast.makeText(LoginActivity.this, "登录失败", Toast.LENGTH_SHORT).show();
                                }
                            }
                        }).start();
                    } else {
                        Toast.makeText(LoginActivity.this, "帐号密码格式错误", Toast.LENGTH_SHORT).show();
                    }
                }
            }
        });
    }
}

登录业务实现包含:监听点击事件,获取输入数据,校验数据,判空处理,登录请求,登录结果展示,可明显看到代码混乱,耦合重的问题。那么如果我们将它改为MVP模式,如何拆分呢?首先要理解MVP模式中Model/View/Presenter分别做什么。

2.如何使用MVP模式

MVP代表Model,View和Presenter。

  • Model很简单, 进行数据加载。如数据库操作,文件查询, 网络请求。
  • View层负责显示数据、用户交互。MVP下Activity和Fragment体现在了这一 层,如加载UI视图、设置监听。
  • Presenter层处理各种逻辑的分发,收到View层UI上的反馈的指令后分发处理逻辑交由Model层做具体的业务操作。

2.1MVP实现之拆分

理解了MVP模式的基本实现原则之后,Login功能就可以拆分为:

1、取值, EditText帐号与密码(明确的View层,不涉及逻辑操作)
2、判空与校验 (Presenter但涉及View, 因为使用帐号与密码,通过传参的形式)
3、登录请求 (名副其实的Model, 处理明显在Presenter层)
4、更新UI (View层) 

我们按照这样的拆分方式重新写一遍登录请求的代码。

Model层

public class LoginModel {
    
    public LoginModel() {
       
    }

    //回调的接口
    public interface OnLoginCallback{
        void onResponse(boolean success);
    }

    public void login(String username,String password,final OnLoginCallback onLoginCallback){
        //..登录请求
        boolean loginResult = false;
        //登录结果
        onLoginCallback.onResponse(loginResult);
    }
}

View层

public class LoginActivity extends AppCompatActivity {
    EditText inputUserName;
    EditText inputPassword;
    Button btnLogin;
    LoginPresenter presenter;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_login);
        inputUserName = this.findViewById(R.id.et_username);
        inputPassword = this.findViewById(R.id.et_password);
        btnLogin = this.findViewById(R.id.bt_login);
        //创建Presenter
        presenter = new LoginPresenter(this);
        btnLogin.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //调用Presenter层,登录逻辑execureLogin()
                presenter.execureLogin(getEditorText(inputUserName),getEditorText(inputPassword));
            }
        });
    }
    //在View层获取输入数据
    private String getEditorText(EditText et) {
        return et.getText().toString();
    }
    //在View层显示结果
    public void notifyLoginResult(boolean loginResult) {
        if (loginResult) {
            showTips("登录成功");
        } else {
            showTips("登录失败");
        }
    }

    public void showTips(String verifyMsg) {
        Toast.makeText(LoginActivity.this, verifyMsg, Toast.LENGTH_SHORT).show();
    }
}

Presenter层

public class LoginPresenter {
    private LoginActivity activity;
    private LoginModel model;
    private String verifyMsg;
    public LoginPresenter(LoginActivity activity) {
        //获取View层
        this.activity = activity;
        //获取model对象
        this.model = new LoginModel();
    }
    public void execureLogin(String username,String password){
        //逻辑类的方法
        boolean verifyBefore = verifyBeforeLogin(username,password);
        if (verifyBefore) {
            //调用login()实现网络请求
             model.login(username, password, new LoginModel.OnLoginCallback() {
                 //实现回调方法
                 @Override
                 public void onResponse(boolean success) {
                     //将结果给到View层
                     activity.notifyLoginResult(success);
                 }
             });
        }else {
            activity.showTips(verifyMsg);
        }
    }

    private boolean verifyBeforeLogin(String username, String password) {
        boolean isEmpty = isEmpty(username) || isEmpty(password);
        boolean isValid = isValid(username) && isValid(password);
        if (isEmpty) {
            verifyMsg = "请输入帐号或密码";
            return false;
        }
        if (isValid) {
            return true;
        }
        verifyMsg = "帐号或密码错误";
        return false;
    }

    private boolean isValid(String s) {
        return Pattern.compile("^[A-Za-z0-9]{3,20}+$").matcher(s).matches();
    }

}

虽然根据MVP将页面拆分了三层,但是代码中耦合的情况还是存在:

  • Presenter持有View(Activity)和Model对象
  • View持有Presenter对象

2.2MVP实现之接口通信

利用接口达到解耦目的,需要View与Presenter分别实现供外部调用的接口。

  • View供Presenter调用的方法有notifyLoginResultshowTips
  • Presenter供View调用的方法有executeLogin

在接口中提供对外部调用的方法,然后分别在View和Presenter中实现它们,最后把持有对象改为接口,具体实现如下: 

定义IPresenter接口类

public interface IPresenter {
    //执行登录的接口
    void execureLogin(String username,String password);
}

 定义IView接口类

public interface IView {
    //显示结果的接口
    void notifyLoginResult(boolean loginResult);
    void showTips(String verifyMsg);
}
//LoginPresenter实现IPresenter接口
public class LoginPresenter implements IPresenter{
    //原来的LoginActivity改为了IView
    private IView activity;
    ...

}

----------------------------------------------------------------------------------------


//LoginActivity 实现IView接口
public class LoginActivity extends AppCompatActivity implements IView{
    //将原来的LoginPresenter改为IPresenter
    IPresenter presenter;

...

}

2.3MVP实现之功能仓库

MVP引入了BaseInterface 与Contract的概念。定义的接口要继承IView与IPresenter, 而且由Contract统一管理。

step1.首先定义公共接口,用于统一规范

public interface IPresenter {
    ...
    //一些公共的接口
}
public interface IView {
     ...
    //一些公共的接口
}

step2.定义的接口要继承IView和IPresenter,由Contract进行统一管理

public interface LoginContract {
    interface View extends IView{
        //显示结果的接口
        void notifyLoginResult(boolean loginResult);
        void showTips(String verifyMsg);
    }

    interface Presenter extends IPresenter{
        void login(String name,String password);
    }
}

step3.实现IView和IPresenter接口

public class LoginActivity extends AppCompatActivity implements IView {
    EditText inputUserName;
    EditText inputPassword;
    Button btnLogin;
    LoginPresenter presenter;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_login);
        inputUserName = this.findViewById(R.id.et_username);
        inputPassword = this.findViewById(R.id.et_password);
        btnLogin = this.findViewById(R.id.bt_login);
        presenter = new LoginPresenter(this);
        btnLogin.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                presenter.execureLogin(getEditorText(inputUserName),getEditorText(inputPassword));
            }
        });
    }

    private String getEditorText(EditText et) {
        return et.getText().toString();
    }

    public void notifyLoginResult(boolean loginResult) {
        if (loginResult) {
            showTips("登录成功");
        } else {
            showTips("登录失败");
        }
    }

    public void showTips(String verifyMsg) {
        Toast.makeText(LoginActivity.this, verifyMsg, Toast.LENGTH_SHORT).show();
    }
}
public class LoginPresenter implements IPresenter {
    //原来的LoginActivity改为了IView
    private IView activity;
    private LoginModel model;
    private String verifyMsg;
    public LoginPresenter(LoginActivity activity) {
        this.activity = activity;
        this.model = new LoginModel();
    }
    public void execureLogin(String username,String password){
        boolean verifyBefore = verifyBeforeLogin(username,password);
        if (verifyBefore) {
             model.login(username, password, new LoginModel.OnLoginCallback() {
                 @Override
                 public void onResponse(boolean success) {
                     activity.notifyLoginResult(success);
                 }
             });
        }else {
            activity.showTips(verifyMsg);
        }
    }

    private boolean verifyBeforeLogin(String username, String password) {
        boolean isEmpty = isEmpty(username) || isEmpty(password);
        boolean isValid = isValid(username) && isValid(password);
        if (isEmpty) {
            verifyMsg = "请输入帐号或密码";
            return false;
        }
        if (isValid) {
            return true;
        }
        verifyMsg = "帐号或密码错误";
        return false;
    }

    private boolean isValid(String s) {
        return Pattern.compile("^[A-Za-z0-9]{3,20}+$").matcher(s).matches();
    }

}

3.MVP模式的优缺点

MVP的优点:

模型与视图完全分离,降低耦合度,我们可以修改视图而不影响模型;
可以将一个Presenter用于多个视图,而不需要改变Presenter的逻辑,提高代码复用。
逻辑放在Presenter中,利于测试驱动开发


MVP缺点:

增加了代码的复杂度,特别是针对小型Android应用的开发,会使程序冗余。
视图和Presenter的交互会过于频繁,一旦视图变更了,presenter也要变更。 

参考

android mvp框架步骤,详解Android中的MVP架构分解和实现_weixin_39848347的博客-CSDN博客

Android:安卓学习笔记之MVP模式的简单理解和使用_JMW1407的博客-CSDN博客

这是一个使用mvp模式实现模拟用户登录的简单Demo。_Davide~苏的博客-CSDN博客

猜你喜欢

转载自blog.csdn.net/weixin_43858011/article/details/124625210