淺析MVP模式

  MVP模式已經流行很長一段時間了,以前只是聽到關於MVP的一些東西,譬如:MVP是MVC的進化版,MVP模式讓View層和Model層完全解耦了。
  這裏先簡單回顧一下Android中MVC模式:M即是代表業務邏輯和實體模型;V即是代碼對應的View,也就是界面;C即是代表控制層,對應着Activity或者Fragment;但是很多時候數據處理都在Activity或者Fragment中,View可以直接訪問Model,這樣就不可避免一些業務邏輯了,而且有許多業務邏輯都是在View層處理的,所以導致很多代碼也就無法重用了。
  MVP模式與MVC模式最大的區別就是View不與Model打交道,而是通過Presenter來與Model交互,而且Presenter實現的邏輯代碼與具體View是沒有關聯的,它不屬於任何具體的View,而只是通過約定好的接口來交互。
  根據一個簡單的例子來對MVP模式進行更深刻的理解,圖1就是要實現的UI效果,一個簡單的登陸界面:
這裏寫圖片描述

圖2是項目文件結構圖:

這裏寫圖片描述

圖3是MVP示意圖和項目中各類的對應關係,以便更好地理解:

這裏寫圖片描述

解析:

  • UserBean是實體類,這個沒什麼可以講的,直接上代碼:
public class UserBean {
    private String name;
    private String password;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getPassword() {
        return password;
    }
    public void setPassword(String password) {
        this.password = password;
    }
}
  • Model部分是負責數據邏輯處理的部分,這個例子比較簡單,就只有登陸這個功能而已,方便我們以後做業務拓展,我們先寫一個UserModel 的接口UserModelInterface,代碼如下:
public interface UserModelInterface {
     void login(String name,String password,LoginListener loginListener);
}

UserModel 實現代碼如下:

public class UserModel implements UserModelInterface {
    @Override
    public void login(String name, String password, LoginListener loginListener) {
        //判斷用戶名是否爲空
        if (TextUtils.isEmpty(name)){
            loginListener.nameIsEmpty();
            return;
        }
        //判斷密碼是否爲空
        if (TextUtils.isEmpty(password)){
            loginListener.passwordIsEmpty();
            return;
        }
        //用戶名和密碼都不爲空的情況下執行登陸操作,這裏就實現登陸的網絡請求代碼了,直接默認登陸成功,如果登陸失敗則回調loginListener.loginFailed();
        loginListener.loginSuccess();
    }
}

LoginListener 這個接口主要是用於Model和Presenter的交互,把Model的業務邏輯處理接口返回出去給Presenter,然後Presenter再通過接口(LoginView)把結果返回給View層,這大概就是MVP的模式的核心了,先不急,先上LoginListener的代碼:

public interface LoginListener {
    void loginSuccess();
    void loginFailed();
    void nameIsEmpty();
    void passwordIsEmpty();
}

結合這個案例來講,無非就兩種結果,登陸成功或者失敗,然後執行登陸之前要判斷用戶名和密碼是否爲空,所以就有以上四個方法了。

  • 接下來看我們用於View(MVPLoginActivity)和Presenter(LoginPresenter)交互的接口–LoginView,這個接口主要是從分析界面來得到的業務邏輯,譬如:用戶有在登陸成功後我們需要進行頁面的跳轉,登陸失敗後的我們需要彈出一個Toast來提示用戶,或者更改界面提示等等。所以就有以下的代碼實現:
public interface LoginView {
    String getName();
    String getPassword();
    void setName(String name);
    void setPassword(String password);
    void loginSuccess();//登錄成功
    void loginFailed();//登錄失敗
    void nameIsEmpty();//名字爲空
    void passwordIsEmpty();//密碼爲空
}
  • 既然說LoginView是用於View和Presenter的交互的,現在問題來了,LoginView是怎麼來連接MVPLoginActivity和LoginPresenter的呢?其實很簡單,只需要MVPLoginActivity去實現LoginView這個接口,直接上MVPLoginActivity代碼:
public class MVPLoginActivity extends AppCompatActivity implements LoginView{

    private AutoCompleteTextView email;
    private EditText passwordEt;
    private Button email_sign_in_button;
    private LoginPresenter loginPresenter=new LoginPresenter(this);//實例化Presenter
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_login);
        email= (AutoCompleteTextView) findViewById(R.id.email);
        passwordEt= (EditText) findViewById(R.id.password);
        email_sign_in_button= (Button) findViewById(R.id.email_sign_in_button);
        email_sign_in_button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                loginPresenter.login();//登錄登錄登錄登錄登錄
            }
        });
    }
    @Override
    public String getName() {
        return email.getText().toString();
    }
    @Override
    public String getPassword() {
        return passwordEt.getText().toString();
    }
    @Override
    public void setName(String name) {
        email.setText(name);
    }
    @Override
    public void setPassword(String password) {
        passwordEt.setText(password);
    }
    @Override
    public void loginSuccess() {
        Log.i("ysc","loginSuccess");
    }
    @Override
    public void loginFailed() {
        Log.i("ysc","loginFailed");
    }
    @Override
    public void nameIsEmpty() {
        Log.i("ysc","nameIsEmpty");
    }
    @Override
    public void passwordIsEmpty() {
        Log.i("ysc","passwordIsEmpty");
    }
}

在這個地方就出現了我們全面說了很多的用於View和Model交互的P(LoginPresenter)這個類了,在界面(MVPLoginActivity )實例化時也實例化LoginPresenter,在點擊按鈕時執行loginPresenter.login(),然後View就可以不用關係其他情況了,然後在下面看LoginPresenter的代碼:

public class LoginPresenter {
    private LoginView loginView;
    private UserModel userModel;

    public LoginPresenter(LoginView loginView) {
        this.loginView = loginView;
        this.userModel=new UserModel();
    }

    public void login(){
    userModel.login(loginView.getName(), loginView.getPassword(),    new LoginListener() {
            @Override
            public void loginSuccess() {
                loginView.loginSuccess();
            }

            @Override
            public void loginFailed() {
                loginView.loginFailed();
            }

            @Override
            public void nameIsEmpty() {
                loginView.nameIsEmpty();
            }

            @Override
            public void passwordIsEmpty() {
                loginView.passwordIsEmpty();
            }
        });
    }
}

先看構造方法

public LoginPresenter(LoginView loginView) {
        this.loginView = loginView;
        this.userModel=new UserModel();
    }

先把Loginview傳進來用戶與view層的交互,再實例化Model(UserModel),前面有說過業務處理都在Model裏,也就是userModel.login()這個方法,然後Model的處理結構通過接口LoginListener來告知LoginPreseter,LoginPresenter再通過Loginview來更新界面,這樣就完成View與Model的完全解耦了。

  • LoginView:就是寫一些界面需要業務邏輯,然後可以實現相對應的UI操作。
  • MVPLoginActivity:單純的界面,實現LoginView接口,並且實例化Presenter。
  • Presenter:LoginPreseter作爲MVPLoginActivity和UserModel的中間層,把通過LoginView拿到的界面數據傳給UserModel處理,UserModel處理完的結果返回回來之後,通過LoginView來更新界面或者做相應的UI操作。
  • MVPLoginActivity和LoginPreseter,LoginPreseter和UserModel之間的交流都是通過接口來處理,前者通過LoginView,後者通過LoginListener。
  • 從以上分析看來MVP模式確實完成了View層和Model的完全解耦

以上就是個人看過很多篇關於MVP方面的博客資料的後,動手寫的demo,順便也寫成博客,歡迎多多指正。

發佈了41 篇原創文章 · 獲贊 44 · 訪問量 11萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章