MVP mode understanding and use

Table of contents

1. Why use the MVP pattern

1.1 Example description

2. How to use the MVP pattern

2.1 Splitting of MVP Implementation

2.2 Interface communication implemented by MVP

2.3 Function warehouse realized by MVP

3. Advantages and disadvantages of the MVP model


1. Why use the MVP pattern

In Android development, the primary responsibility of Activity is to load the layout of the application and initialize the user interface, accept and process operation requests from users. However, as the complexity of the interface and logic continues to increase, the responsibilities of the Activity class continue to increase and become large and bloated. Then we need to use the MVP model to solve the problems of confusion, redundancy, and heavy coupling.

1.1 Example description

The following is a demo of user login

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();
                    }
                }
            }
        });
    }
}

The implementation of the login service includes: listening to click events, obtaining input data, verifying data, processing empty judgment, login request, and displaying login results. You can clearly see the problems of code confusion and heavy coupling. So if we change it to MVP mode, how to split it? First of all, we must understand what Model/View/Presenter do in MVP mode.

2. How to use the MVP pattern

MVP stands for Model, View and Presenter.

  • Model is very simple, for data loading. Such as database operations, file queries, network requests.
  • The View layer is responsible for displaying data and user interaction. Activities and Fragments under MVP are reflected in this layer, such as loading UI views and setting monitors.
  • The Presenter layer handles the distribution of various logics. After receiving the feedback instructions from the View layer UI, the distribution processing logic is handed over to the Model layer for specific business operations.

2.1 Splitting of MVP Implementation

After understanding the basic implementation principles of the MVP pattern, the Login function can be split into:

1. Value, EditText account number and password (clear View layer, does not involve logical operations)
2. Null judgment and verification (Presenter but involves View, because the account number and password are used, through the form of parameter passing)
3. Login request ( A veritable Model, the processing is obviously in the Presenter layer)
4. Update the UI (View layer) 

We rewrite the login request code in this way.

Model layer

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 layer

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 layer

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();
    }

}

Although the page is split into three layers according to MVP, the coupling in the code still exists:

  • Presenter holds View (Activity) and Model objects
  • View holds Presenter object

2.2 Interface communication implemented by MVP

Using the interface to achieve the purpose of decoupling requires View and Presenter to implement interfaces for external calls respectively.

  • The methods that View calls for Presenter are notifyLoginResultand showTips;
  • There are methods that Presenter can call for View executeLogin.

Provide methods for external calls in the interface, then implement them in View and Presenter respectively, and finally change the holding object into an interface. The specific implementation is as follows: 

Define the IPresenter interface class

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

 Define the IView interface class

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.3 Function warehouse realized by MVP

MVP introduces the concepts of BaseInterface and Contract. The defined interface should inherit from IView and IPresenter, and be managed uniformly by Contract.

step1. First define the public interface for unified specification

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

step2. The defined interface should inherit IView and IPresenter, and be managed by 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. Implement the IView and IPresenter interfaces

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. Advantages and disadvantages of the MVP model

Advantages of MVP:

The model is completely separated from the view to reduce the degree of coupling. We can modify the view without affecting the model; we can
use one Presenter for multiple views without changing the logic of the Presenter to improve code reuse.
The logic is placed in the Presenter, which is conducive to test-driven development


MVP disadvantages:

Increased code complexity, especially for the development of small Android applications, will make the program redundant.
The interaction between the view and the Presenter will be too frequent. Once the view changes, the presenter will also change. 

reference

The steps of the android mvp framework, explaining the decomposition and implementation of the MVP architecture in Android - Weixin_39848347's Blog - CSDN Blog

Android: Simple understanding and use of the MVP mode of Android study notes - JMW1407's Blog - CSDN Blog

This is a simple demo that uses mvp mode to simulate user login. _Davide~Su's Blog-CSDN Blog

Guess you like

Origin blog.csdn.net/weixin_43858011/article/details/124625210
MVP