1. 程式人生 > >淺談android開發中的MVP模式

淺談android開發中的MVP模式

這裡寫圖片描述

看到MVP,大家肯定會想什麼是MVP呢?這個我可以肯定的告訴大家MVP(Most Valuable Player)是最有價值球員的意思,這當然是開玩笑了。之所以會出現MVP這種架構模式,是因為我相信大家在開發App時,肯定會發現,Activity的負擔非常重,既要初始化控制元件,又要寫一些邏輯操作的展示等等,有時候很多Activity中的程式碼都充當了Controller和Model的角色,所以你會發現Activity違背單一職責原則,負擔過重。所以,就出現了這麼一種架構模式,叫MVP,並不是最有價值球員哦。

什麼是MVP架構

MVP就是Model-View-Presenter,MVP是從經典的模式MVC演變而來,它們的基本思想有相通的地方:Controller/Presenter負責邏輯的處理,Model提供資料,View負責顯示。作為一種新的模式,MVP與MVC有著一個重大的區別:在MVP中View並不直接使用Model,它們之間的通訊是通過Presenter (MVC中的Controller)來進行的,所有的互動都發生在Presenter內部,而在MVC中View會直接從Model中讀取資料而不是通過 Controller。在MVC裡,View是可以直接訪問Model的!從而,View裡會包含Model資訊,不可避免的還要包括一些業務邏輯。 在MVC模型裡,更關注的Model的不變,而同時有多個對Model的不同顯示,及View。所以,在MVC模型裡,Model不依賴於View,但是View是依賴於Model的。不僅如此,因為有一些業務邏輯在View裡實現了,導致要更改View也是比較困難的,至少那些業務邏輯是無法重用的。用流程圖的方式解釋就更清楚了:
這裡寫圖片描述

MVP和MVC的區別,及MVP是如何解決MVC的問題?

MVP架構:
View: 對應於Activity,負責View的繪製以及與使用者互動
Model: 依然是業務邏輯和實體模型
Presenter: 負責完成View於Model間的互動
這裡寫圖片描述

View不直接與Model互動,而是通過與Presenter互動來與Model間接互動。
Presenter與View的互動是通過介面來進行的。
通常View與Presenter是一對一的,但複雜的View可能繫結多個Presenter來處理邏輯。

MVC架構:
View:對應於佈局檔案
Model:業務邏輯和實體模型
Controllor:對應於Activity

這裡寫圖片描述

View可以與Model直接互動。
Controller是基於行為的,並且可以被多個View共享。
可以負責決定顯示哪個View。

總結解釋一下就是說:
從MVC到MVP的一個轉變,就是減少了Activity的職責,減輕了它的負擔,簡化了Activity中的程式碼和一些操作,將邏輯程式碼提取到了Presenter中進行處理,降低了其耦合度。

進一步的解釋:

在MVP裡,Presenter完全把Model和View進行了分離,主要的程式邏輯在Presenter裡實現。而且,Presenter與具體的View是沒有直接關聯的,而是通過定義好的介面進行互動,從而使得在變更View時候可以保持Presenter的不變,即重用! 不僅如此,我們還可以編寫測試用的View,模擬使用者的各種操作,從而實現對Presenter的測試–而不需要使用自動化的測試工具。 我們甚至可以在Model和View都沒有完成時候,就可以通過編寫Mock Object(即實現了Model和View的介面,但沒有具體的內容的)來測試Presenter的邏輯。 在MVP裡,應用程式的邏輯主要在Presenter來實現,其中的View是很薄的一層。因此就有人提出了Presenter First的設計模式,就是根據User Story來首先設計和開發Presenter。在這個過程中,View是很簡單的,能夠把資訊顯示清楚就可以了。在後面,根據需要再隨便更改View,而對Presenter沒有任何的影響了。 如果要實現的UI比較複雜,而且相關的顯示邏輯還跟Model有關係,就可以在View和Presenter之間放置一個Adapter。由這個 Adapter來訪問Model和View,避免兩者之間的關聯。而同時,因為Adapter實現了View的介面,從而可以保證與Presenter之間介面的不變。這樣就可以保證View和Presenter之間介面的簡潔,又不失去UI的靈活性。 在MVP模式裡,View只應該有簡單的Set/Get的方法,使用者輸入和設定介面顯示的內容,除此就不應該有更多的內容,絕不容許直接訪問Model–這就是與MVC很大的不同之處。

MVP的優點
1.降低耦合度,隱藏資料,Activity中程式碼更簡潔
2.模組職責劃分明顯3.方便測試驅動開發4.程式碼複用度較高5.程式碼靈活性
MVP架構模式例項

這個例項是根據使用者id獲取使用者資訊並展示的一個過程,其中獲取使用者資訊用了一個執行緒進行了模擬獲取。希望大家能夠看懂,並對大家有所幫助。
我們先看一下MVP目錄結構圖
這裡寫圖片描述

1、Model層
首先是一個javabean User實體類

package net.loonggg.mvpdemo.bean;

public class User {
    private String name;
    private String id;
    private String sex;
    private String age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public String getAge() {
        return age;
    }

    public void setAge(String age) {
        this.age = age;
    }

}

Model層抽象介面實現:

package net.loonggg.mvpdemo.model;

import net.loonggg.mvpdemo.bean.User;

public class GetUserInfo implements IGetUser {

    @Override
    public void getUserInfo(final int id, final OnUserInfoListener listener) {
        // 模擬子執行緒耗時操作
        new Thread() {
            @Override
            public void run() {
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                // 模擬資訊獲取成功
                if (id == 1) {
                    User user = new User();
                    user.setName("非著名程式設計師");
                    user.setAge("26");
                    user.setSex("男");
                    user.setId("1");
                    listener.getUserInfoSuccess(user);
                } else {
                    listener.getUserInfoFailed();
                }
            }
        }.start();
    }

}

Model層抽象介面

package net.loonggg.mvpdemo.model;

public interface IGetUser {
    public void getUserInfo(int id, OnUserInfoListener listener);
}

2、View層
我們都知道Presenter與View互動是通過介面,所以我們需要定義一個IShowUserView的介面,這個介面封裝的方法基本上都跟檢視展示有關。

package net.loonggg.mvpdemo.view;

import net.loonggg.mvpdemo.bean.User;

public interface IShowUserView {
    void showLoading();

    void hideLoading();

    void toMainActivity(User user);

    void showFailedError();
}

3、Presenter層
Presenter是Model和View之間互動的橋樑,裡面有一些業務邏輯的操作。

package net.loonggg.mvpdemo.presenter;

import net.loonggg.mvpdemo.bean.User;
import net.loonggg.mvpdemo.model.GetUserInfo;
import net.loonggg.mvpdemo.model.IGetUser;
import net.loonggg.mvpdemo.model.OnUserInfoListener;
import net.loonggg.mvpdemo.view.IShowUserView;
import android.os.Handler;

public class UserInfoPresenter {
    private IGetUser iGetUser;
    private IShowUserView iShowUserView;
    private Handler mHandler = new Handler();

    public UserInfoPresenter(IShowUserView iShowUserView) {
        this.iShowUserView = iShowUserView;
        this.iGetUser = new GetUserInfo();
    }

    public void getUserInfoToShow(int id) {
        iShowUserView.showLoading();
        iGetUser.getUserInfo(id, new OnUserInfoListener() {

            @Override
            public void getUserInfoSuccess(final User user) {
                // 需要在UI執行緒執行
                mHandler.post(new Runnable() {
                    @Override
                    public void run() {
                        iShowUserView.toMainActivity(user);
                        iShowUserView.hideLoading();
                    }
                });
            }

            @Override
            public void getUserInfoFailed() {
                mHandler.post(new Runnable() {
                    @Override
                    public void run() {
                        iShowUserView.showFailedError();
                        iShowUserView.hideLoading();
                    }
                });
            }
        });
    }

}

4、Activity中的呼叫

package net.loonggg.mvpdemo;

import net.loonggg.mvpdemo.bean.User;
import net.loonggg.mvpdemo.presenter.UserInfoPresenter;
import net.loonggg.mvpdemo.view.IShowUserView;
import android.app.Activity;
import android.app.ProgressDialog;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;

public class MainActivity extends Activity implements IShowUserView {
    private Button btn;
    private TextView name_tv, age_tv, sex_tv;
    private ProgressDialog pd = null;
    private UserInfoPresenter userInfoPresenter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        userInfoPresenter = new UserInfoPresenter(this);
        btn = (Button) findViewById(R.id.btn);
        name_tv = (TextView) findViewById(R.id.name_tv);
        age_tv = (TextView) findViewById(R.id.age_tv);
        sex_tv = (TextView) findViewById(R.id.sex_tv);
        pd = new ProgressDialog(this);
        pd.setMessage("正在載入……");

        btn.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View v) {
                userInfoPresenter.getUserInfoToShow(1);
            }
        });
    }

    @Override
    public void showLoading() {
        pd.show();
    }

    @Override
    public void hideLoading() {
        pd.cancel();
    }

    @Override
    public void toMainActivity(User user) {
        name_tv.setText(user.getName());
        age_tv.setText(user.getAge());
        sex_tv.setText(user.getSex());
    }

    @Override
    public void showFailedError() {
        Toast.makeText(this, "獲取資訊有誤", Toast.LENGTH_SHORT).show();
    }

}

結語:看完例項程式碼,有點感覺了吧?俗話說好記性不如爛筆頭,看不如寫,試著自己去寫一個,領會一下其中的精神,相信你會豁然開朗。當然有人說這麼做,是不是又多了一層,感覺又麻煩了,是嗎?降低了耦合度,提取了程式碼,並增加了複用,程式碼更簡潔,其實好處還是很多的。