MVP模式

轉:http://blog.csdn.net/qq_31852701/article/details/52946127java

首先MVP 是從經典的MVC架構演變而來,那咱們是否是要先說下何爲MVC模式?面試

 

系統C/S(Client/Server)三層架構模型:正則表達式

1)視圖層(View):通常採用XML文件對應用的界面進行描述,使用的時候能夠直接引入,極爲方便,能夠的大大縮短開發時間,也可使用JavaScript+HTML等的方式做爲View層,固然這裏須要進行Java和JavaScript之間的通訊,幸運的是,Android提供了很是方便的通訊實現。業務邏輯層(BLL):它的關注點主要集中在業務規則的制定、業務流程的實現等與業務需求有關的系統設計,也便是說它是與系統所應對的領域(Domain)邏輯有關,不少時候,也將業務邏輯層稱爲領域層。數據庫

2)控制層(Controller):Android的控制層的重任一般落在了衆多的Acitvity的肩上,這句話也就暗含了不要在Acitivity中寫代碼,要經過Activity交割Model業務邏輯層處理。編程

3)模型層(Model):對數據庫的操做、以及其餘和數據有關的的操做都應該在Model裏面處理,固然對業務計算等操做也是必須放在的該層的。就是應用程序中二進制的數據。設計模式

   三層結構架構三層間的交互及做用以下圖所示:緩存

 

傳統的MVC模式是很不錯,咱們也很熟悉,畢竟用了這麼多年了。(忽然想到以前去一家公司面試他問個HelloWord的Android項目是否是MVC,我說不是,他說回答錯誤,阿西吧!)網絡

在Android項目上你會發現Activity的責任過重,什麼東西都要放在Activity中,最終致使了Activity太過臃腫。雖然能抽的都抽出來了,可是會發現代碼仍是不少,試想下上千行代碼尚未註釋,能不暈?即便是本身寫的,過些日子去看也有些暈暈的吧?架構

尤爲代碼敲完,一個月後需求又改了,從600、700行代碼中找到要修改的地方也是要一點功夫的。框架

爲了給Activity減輕壓力,這時候MVP出現了!

 

MVP有什麼好處,爲何要用MVP呢?

網上搜下一大堆MVP的各類好處,寶寶總結下主要有如下幾點:

 

  • 代碼解耦
  • 結構清晰
  • 可複用
  • 擴展性高
  • 方便進行單元測試
在MVP中View並不直接使用Model,它們之間的通訊是經過Presenter (MVC中的Controller)來進行的,全部的交互都發生在Presenter內部,而在MVC中View會從直接Model中讀取數據而不是經過 Controller。
在MVP裏,Presenter徹底把Model和View進行了分離,主要的程序邏輯在Presenter裏實現。並且,Presenter與具體的View是沒有直接關聯的,而是經過接口進行交互,從而使得在變動View時候能夠保持Presenter的不變,能夠屢次複用。
在MVP裏,應用程序的邏輯主要在Presenter來實現,其中的View是很薄的一層,只應該有簡單的Set/Get的方法,用戶輸入和設置界面顯示的內容,除此就不該該有更多的內容,毫不允許直接訪問Model。
MVP主要解決就是把邏輯層抽出來成P層,要是遇到需求邏輯上的更改就能夠只須要修改P層了或者遇到邏輯上的大改咱們能夠直接重寫一個P也能夠,不少開發人員把全部的東西都寫在了Activity/Fragment裏面這樣一來遇到頻繁改需求或者邏輯愈來愈複雜的時候,Activity /Fragment裏面就會出現過多的混雜邏輯致使出錯,因此MVP模式對於APP來對控制邏輯和UI的解耦來講是一個不錯的選擇。

上面說邏輯是在Presenter中處理的,假設這是一個登錄界面,如要輸入帳號密碼,那麼對帳號密碼的是否爲空判斷以及正則表達式判斷等也要放在Presenter中,畢竟這些都是邏輯。
而後判斷都成功的話(下面的操做都是在Presenter中進行的),先調用view層的方法,讓ProgressDialog顯示出來,而後調用model層的網絡請求,結果的話在presenter中回調。在回調裏面分別作ProgressDialog消失的處理,而後成功的話調用view層的方法,進入主界面。失敗的話調用view層的方法,提示失敗Toast之類的。
這就是最簡單的一個流程了。
 
 
MVP是什麼?怎麼實現?
 
下圖是本人Demo中的代碼,就一個頁面而已,然而一個頁面居然有這麼多的類,11個,我勒個去,嚇死寶寶了,這麼多的類,說實話一個類就能解決了。不慌,下面讓咱們仔細分析爲何這麼多類

首先介紹下每一個類是幹嗎的:
  • BuyBooActivity是咱們的Activity
  • BaseActivity是Activity的基類
  • BasePresenter是Presenter的基類
  • BuyBookBean是咱們的bean,也就是傳說中的實體類,幾個成員變量,自動生成一堆get、set方法的那個類
  • IBuyBookView也就是BuyBooActivity的接口
  • BuyBookPresenter也就是這個Actvity的Presenter
  • IBuyBookPresenter是BuyBookPresenter的接口
  • BuyBookModel是這個Activity的數據層
  • IBuyBookModel是BuyBookModel的接口
  • BuyBookAdapter是BuyBooActivity裏面ListView的適配器
  • ValueCallBack,是一個通用的回調接口
這裏接口都以I開頭,每一個model,presenter,還有Activity的view,都對應着一個接口,有些人可能會問了,幹嗎寫接口,一下多了這麼多類,看的都懵逼了(好比之前有一個同事,他一直說項目用MVP,可是他不知道什麼是MVP,直接把數據層寫在了P中,估計是他覺得bean纔是model吧。我也不跟他說,畢竟人家工資比較高。說道這裏忽然想起了他之前的MVVM模式,第一個M是bean,他說這是model,第二個M以及一個V他說這是viewmodel,結果放的是全部的網絡請求,最後一個V他放的是Aciivty和Fragment了。好了笑話說到這裏,繼續咱們的主題)

爲何寫接口?
問這個問題的童鞋應該去複習設計模式的六大基本原則了,咱們要控制項目而不是項目控制咱們,咱們要進行的是面向接口的編程,用抽象(或者接口)搭建框架,用實現擴展細節。固然接口也不能濫用,都要看具體狀況,這裏本人是按照最高標準來寫的。
 
Google官方MVP示例項目是把寶寶上面的IBuyBookPresenter以及IBuyBookView這2個接口放到了IBuyBookContract這個協議類中,其實是同樣的,這就看我的喜愛了,各有各的優缺點。好比寶寶的代碼改爲有協議類的這種就以下代碼所示,看到了吧,實際上是同樣的。
public interface IBuyBookContract  
{  
    interface IBuyBookView  
    {  
        void showToast(String msg);  
  
        void refreshAdapter();  
  
        void onEmpty();  
    }  
  
    interface IBuyBookPresenter  
    {  
  
        List<BuyBookBean> getAdapterData();  
    }  
}  

 


 



M(Model)
數據層,和MVC中的M同樣,用來放數據的處理(好比網絡請求,緩存等)。
 
V(View)
負責UI具體實現展示。好比Presenter派發過來一個動做是showDialog顯示進度命令,那麼咱們這個View就負責實現具體UI。
 
P(Presenter)
負責處理業務邏輯代碼,處理Model數據,而後將處理完的數據分發到View層。

 

 

下面開始貼代碼講解

 

先來說View IBuyBookView

這裏主要是包含了Activity的一系列關於UI操做,而後用咱們的Activity是實現,這樣Presenter就能夠調用了。

 

public interface IBuyBookView {  
    /** 
     * 提示toast 
     */  
    void showToast(String msg);  
  
    /** 
     * 刷新adapter 
     */  
    void refreshAdapter();  
  
    void onEmpty();  
}  

 

 

接下來說Model IBuyBookModel  BuyBookModel

 

主要是寫了幾個方法供BuyBookModel去實現

 

public interface IBuyBookModel {  
    /** 
     * 獲取模擬數據 
     */  
    void getTestData(ValueCallBack<List<BuyBookBean>> callBack);  
  
  
    /** 
     * 返回本地adapter數據 
     * @return 
     */  
    List<BuyBookBean> getAdapterData();  
}  

 


 

 

這裏實現了IBuyBookModel,而後模擬了下網絡請求,用隨機來模擬請求成功與失敗,因此若是有童鞋下載Demo發現進去怎麼空白一片,那麼請退出來多近幾回,寶寶曾連續5次進去都是空白的,這運氣也太背了吧。

public class BuyBookModel implements IBuyBookModel {  
    private List<BuyBookBean> listData;  
  
  
    public BuyBookModel() {  
        this.listData = new ArrayList<>();  
    }  
  
    @Override  
    public void getTestData(final ValueCallBack<List<BuyBookBean>> callBack) {  
        new Handler().postDelayed(new Runnable() {  
            @Override  
            public void run() {  
                List<BuyBookBean> list = new ArrayList<>();  
                list.add(new BuyBookBean("趙雲", 1, "09-27 09:11"));  
                list.add(new BuyBookBean("趙雲、隔壁老王、小王、典韋、貂蟬、林芳、曹操、劉備、關羽、黃忠、張飛、諸葛孔明", 10, "09-27 09:11"));  
                list.add(new BuyBookBean("黃忠、孫權、大喬", 50, "09-27 09:11"));  
                list.add(new BuyBookBean("大喬、小喬、貂蟬、孫尚香", 300, "09-27 09:11"));  
  
                Random rd = new Random();  
                int N = rd.nextInt(10);  
                if (N > 5) {  
                    callBack.onSuccess(list);  
                } else {  
                    callBack.onFail("拒絕請求");  
                }  
            }  
        }, 1000);  
    }  
  
  
    @Override  
    public List<BuyBookBean> getAdapterData() {  
        return listData;  
    }  
}  

 


 
 

這裏貼上樓上那個回調接口,這裏用了泛型,爲何呢?爲了多多複用。

public interface ValueCallBack<T> {  
    void onSuccess(T t);  
  
    void onFail(String code);  
}  

 


接下來說Presenter BasePresenter IBuyBookPresenterBuyBookPresenter

這個是全部Presenter的基類,裏面有個initData()方法,基本每一個Presenter都要處理網絡請求吧,因此我就弄了這麼一個基類,至於爲何是抽象的而不是接口,這是由於抽象方便點吧,若是咱們往抽象類中添加新的方法(該方法不是抽象的),能夠給他提供默認的實現,並且不要修改現有的代碼,可是若是是接口的話,就要修改現有的代碼了。指不定咱們之後要往這裏加什麼呢。

這裏爲何用了個泛型?爲了讓人一看這個Presenter就知道這對應着哪一個Activity,實際上這能夠不加的,可是我以爲加上去更好。便於後來人也便於本身之後再來修改這個類。

 

public abstract class BasePresenter<T extends BaseActivity> {  
  
    abstract void initData();  
}  

 

 

 

這裏主要寫了個方法,以供BuyBookPresenter實現

public interface IBuyBookPresenter {  
  
    List<BuyBookBean> getAdapterData();  
}  

 

這裏首先實現現了IBuyBookPresenter繼承了BasePresenter,而後重寫一些方法。這裏的構造方法是重點,在構造方法中,咱們須要傳入一個IBookView,實際上咱們的Activity已經實現IBookView了,因此這裏實際上傳的是具體的Activity,也就是this就好了。而後model咱們能夠直接new出來用,這裏就new了。

在initData中咱們是進行了具體的網絡請求,網絡請求咱們是否是要彈一個Dialog出來,直接在這mView.loading();調用就好了。而後請求成功onSuccess()裏面讓Dialog消失,提醒適配器刷新。失敗的話onFail()裏面提示Dialog消失,而後ListView設置失敗頁面什麼的。

 

public class BuyBookPresenter extends BasePresenter<BuyBookActivity> implements IBuyBookPresenter {  
  
    private IBuyBookView  mView;  
    private IBuyBookModel mModel;  
  
    public BuyBookPresenter(IBuyBookView iBuyBookView) {  
        this.mView = iBuyBookView;  
        this.mModel = new BuyBookModel();  
    }  
  
  
    @Override  
    public List<BuyBookBean> getAdapterData() {  
        return mModel.getAdapterData();  
    }  
  
    @Override  
    public void initData() {  
        //在這裏彈出loading  
        mModel.getTestData(new ValueCallBack<List<BuyBookBean>>() {  
            @Override  
            public void onSuccess(List<BuyBookBean> buyBookBeen) {  
                //在這裏取消loading  
                //簡單數據操做能夠在presenter裏完成  
                mModel.getAdapterData().addAll(buyBookBeen);  
                mView.refreshAdapter();  
            }  
  
            @Override  
            public void onFail(String code) {  
                //在這裏取消loading  
                mView.showToast(code);  
                mView.onEmpty();  
            }  
        });  
    }  
}  

 

 

接下來說咱們的Activity BuyBookActivity BaseActivity

這是咱們Acticity的基類,能夠看到這裏有個泛型,注意了,前方高能。這個泛型還必須繼承BasePresenter,這個首先爲了讓人一看到這個Activity就知道對應着那個Presenter;其次最重要的就是爲了下面那個成員變量basepresenter,咱們寫一個抽象的方法要求返回泛型T,而這個泛型T又繼承了BasePresenter,那麼咱們就獲得了具體Presenter的成員變量,能夠直接用這個成員變量來調用Presenter中的方法了。

 

public abstract class BaseActivity<T extends BasePresenter> extends Activity {  
      
    protected T basepresenter;  
  
    @Override  
    protected void onCreate(Bundle savedInstanceState) {  
        super.onCreate(savedInstanceState);  
        setContentView(getLayout());  
        initView();  
        basepresenter = initPresent();  
        onPrepare();  
    }  
  
    abstract T initPresent();  
  
    abstract int getLayout();  
  
    abstract void initView();  
  
    abstract void onPrepare();  
}  

 

這個就是最終具體的Activity了,能夠看到這裏都沒什麼邏輯,基本都是一些重寫的方法。

 

 

public class BuyBookActivity extends BaseActivity<BuyBookPresenter> implements IBuyBookView  
{  
  
    private ListView mListView;  
    private BuyBookAdapter mAdapter;  
  
    @Override  
    BuyBookPresenter initPresent()  
    {  
        return new BuyBookPresenter(this);  
    }  
  
    @Override  
    int getLayout()  
    {  
        return R.layout.activity_main;  
    }  
  
    @Override  
    void initView()  
    {  
        mListView = (ListView) findViewById(R.id.listview);  
    }  
  
    @Override  
    void onPrepare()  
    {  
        mAdapter = new BuyBookAdapter(this, basepresenter.getAdapterData());  
        mListView.setAdapter(mAdapter);  
        basepresenter.initData();  
    }  
  
    @Override  
    public void showToast(String msg)  
    {  
        Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();  
    }  
  
    @Override  
    public void refreshAdapter()  
    {  
        mAdapter.notifyDataSetChanged();  
    }  
  
    public void onEmpty()  
    {  
        mListView.setEmptyView(null);  
    }  
}  

 

最後雖然MVP模式有許多好處,可是有一個致命的缺點就是類太多,原本一個類最多變成了7個類,最少變成6個類(使用Contract協議類)。因此並非全部的頁面都要用MVP模式的,很簡單的頁面就不必了,浪費時間是否是。

 

爲何MVP模式利於單元測試?

Presenter將邏輯和UI分開了,裏面沒有Android代碼,都是純純的java代碼。咱們能夠直接對Presenter寫Junit測試

相關文章
相關標籤/搜索