Android之MVP設計模式


一、概述

MVP設計模式的前身是MVC,這個無需再議

在安卓工程中MVC對應關系如下:

Layout->View : 對應布局文件
Activity->Controller,View (其中activity分的並不是特別清楚)
各種業務邏輯實體類->Model

MVP的基本概念是:

MVP 指的是Model,View,Presenter(交互器/表示器),是從經典的模式MVC演變而來,它們的基本思想有相通的地方:Controller/Presenter負責邏輯的處理,Model提供數據,View負責顯示。

二、MVP和MVC的區別

MVP與MVC有着一個重大的區別:在MVP中View並不直接使用Model,它們之間的通信是通過Presenter (MVC中的Controller)來進行的,所有的交互都發生在Presenter內部,而在MVC中View會直接從Model中讀取數據而不是通過 Controller。

給個圖最能提現區別:

圖的上半部分是MVP模式 -- 可以看出 View 和Model 並不直接交互, 而是通過presenter作為中間人去 協調工作。這么做的好處是使得代碼更加清晰明了,分工明確。

圖的下半部分是MVC模式 -- 控制器controller(Activity)直接操作了 view 以及Model,也就是說運行View 和Model直接通信了。

總結MVP和MVC的優缺點如下:

1:MVC允許 View 和 Model 直接通訊。
2:MVP 中,View和 Model 的交互都在Presenter 內部來完成
3:MVP 中,View 通常會抽象化出來一系列的接口。(面向接口編程)
4:MVP 相對於 MVC 而言,大大降低了耦合度(Activity 不再進行復雜的操作),層級更明顯,更利於單元測試。
5:MVP 的缺點:類文件會爆炸(極具增加),有一定的學習成本。


三、MVP的設計原則 -- 怎么實現一個MVP設計模式?

1:一個 Activity對應一個 View (把activity當成MPV的view進行操作)
2:通常情況下,一個 View對應一個 Presenter,在業務復雜時,一個 View可對應多個 Presenter.
3.通常情況下將 View 與 Presenter抽象成接口。

四、例子

工程結構

 

MVP的 presenter 定義了業務邏輯實現, 它其實有 Model 和 View 交互完成的業務邏輯

然后在MainActivity中顯示頁面結果

 

**
 * MVP 中的Activity 其實就相當於 頁面 用來顯示最終結果的
 *
 * 它直接 根據布局 控件 來操作 布局屬性
 */
public class MainActivity extends AppCompatActivity implements UserView{

    private Toolbar toolbar;
    private ClearEditText etUserName;
    private ClearEditText etPassword;
    private Button btn;
    private TextView tvResult;

    private UserPresenter userPresenter;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        userPresenter = new UserPresenterImp(this);

        initialize();
        setSupportActionBar(toolbar);
        btn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //模擬網絡延遲登陸
                new Handler().postDelayed(new Runnable() {
                    @Override
                    public void run() {
                        userPresenter.login();
                    }
                },500);

            }
        });
    }

    @Override
    public String getUserName() {
        return etUserName.getText().toString();
    }

    @Override
    public String getUserPwd() {
        return etPassword.getText().toString();
    }

    @Override
    public void onSuccess(UserBean userBean) {
        tvResult.setText(userBean.address);
    }

    @Override
    public void onError() {
        Toast.makeText(this,"用戶名或者密碼錯誤",Toast.LENGTH_SHORT).show();
    }

    @Override
    public void onUserNameError() {
        etUserName.setError("用戶名不能為空");//這個onError是在EditText窗口顯示的
    }

    @Override
    public void onUserPwdError() {
        etPassword.setError("密碼不能為空");
    }

    private void initialize() {
        toolbar = (Toolbar) findViewById(R.id.toolbar);
        etUserName = (ClearEditText) findViewById(R.id.etUserName);
        etPassword = (ClearEditText) findViewById(R.id.etPassword);
        btn = (Button) findViewById(R.id.btn);
        tvResult = (TextView) findViewById(R.id.tvResult);
    }
}

 


public
class UserPresenterImp implements UserPresenter { private UserView userView; public UserPresenterImp(UserView userView){ this.userView = userView; } @Override public void login() { String username = userView.getUserName(); String password = userView.getUserPwd(); if(TextUtils.isEmpty(username)){ userView.onUserNameError(); return; } if(TextUtils.isEmpty(password)){ userView.onUserPwdError(); return; } if("123".equals(username)&&"aaa".equals(password)){ String result = "{\n" + " \"username\": \"zccq\",\n" + " \"password\": \"1\",\n" + " \"address\": \"中國\"\n" + "}"; UserBean userBean = new Gson().fromJson(result, UserBean.class); userView.onSuccess(userBean); }else{ userView.onError(); } } }

運行效果圖:

源碼下載地址:https://yunpan.cn/cu7JQWCwLGLum (提取碼:4195)

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM