天天看點

Android開發中的MVP架構

最近越來越多的人開始談論架構。我周圍的同僚和工程師也是如此。盡管我還不是特别深入了解mvp和ddd,但是我們的新項目還是決定通過mvp來建構。

這篇文章是我通過研究和學習各種文章以及專題讨論所總結出來的,它包括以下幾點:

為什麼越來越多的人開始關注架構?

首先,mvp是什麼?

哪種架構才是最好的,mvc,mvvm還是mvp?

mvp的利與弊

show me the code!!!代碼展示

不幸的,這篇文章将不包括:

詳細生動的代碼示例

如何編寫測試代碼

最後,我将告訴你如何更進一步學習這些專題。

順便提一下,我于上周在當地的一個研讨會上對mvp架構進行了相關演講。這篇文章與當時的演講内容相差無幾。

介紹~activity是上帝類~

首先,讓我們思考一下為什麼在android開發中如此迫切地需要一個清晰的軟體架構。

該段摘自“代碼大全第二版”:

避免建立神類。避免建立無所不知,無所不能的上帝類。如果一個類需要花費時間從其他類中通過get()和set()檢索資料(也就是說,需要深入業務并且告訴它們如何去做),是以是否應該把這些功能函數更好的組織到其它類而不是上帝類中。(riel 1996)

上帝類的維護成本很高,你很難了解正在進行的操作,并且難以測試和擴充,這就是為什麼要避免建立上帝類的黃金法則。

然而,在android開發中,如果你不考慮架構的話,activity類往往會越來越大。這是因為,在android中,允許view和其它線程共存于activity内。其實最大的問題莫過于在activity中同時存在業務邏輯和ui邏輯。這會增加測試和維護的成本。

Android開發中的MVP架構

activity是上帝

這是為什麼需要清晰架構的原因之一。不僅會造成activity的臃腫,還會引起其他問題,如使activity和fragment的生命周期變複雜,以及資料綁定等。

什麼是mvp?

mvp代表model,view和presenter。

view層負責處理使用者事件和視圖部分的展示。在android中,它可能是activity或者fragment類。

model層負責通路資料。資料可以是遠端的server api,本地資料庫或者sharedpreference等。

presenter層是連接配接(或适配)view和model的橋梁。

下圖是基于mvp架構的模式之一。view是ui線程。presenter是view與model之間的擴充卡。usecase或者domain在model層中,負責從實體擷取或載入資料。依賴規則如下:

Android開發中的MVP架構

the dependency injection

關鍵是,高層接口不知道底層接口的細節,或者更準确地說,高層接口不能,不應該,并且必須不了解底層接口的細節,是(面向)抽象的,并且是細節隐藏的。

Android開發中的MVP架構

the higher interfaces do not know about the details of the lower ones

依賴規則?

uncle bob的“the clean architecture”描述了依賴的規則是什麼。

同心圓将軟體劃分為不同的區域,一般的,随着層級的深入,軟體的等級也就越高。外圓是實作機制,内圓是核心政策。

這是上面片文章的摘要:

enitities:

可以是一個持有方法函數的對象

可以是一組資料結構或方法函數

它并不重要,能在項目中被不同應用程式使用即可

use cases

包含特定于應用程式的業務規則

精心編排流入entity或從entity流出的資料

指揮entity直接使用項目範圍内的業務規則,進而實作use case的目标

presenters controllers

将use case和entity中的資料轉換成格式最友善的資料

外部系統,如資料庫或網頁能夠友善的使用這些資料

完全包含gui的mvc架構

external interfaces, ui, db

所有的細節所在

如資料庫細節,web架構細節,等等

mvc,mvp還是mvvm?

那麼,哪一個才是最好的呢?哪一個比其他的更優秀呢?我能隻選擇一個嗎?

答案是,no。

這些模式的動機都是一樣的。那就是如何避免複雜混亂的代碼,讓執行單元測試變得容易,創造高品質應用程式。就這樣。

當然,遠不止這三種架構模式。而且任何一種模式都不可能是銀彈,他們隻是架構模式之一,不是解決問題的唯一途徑。這些隻是方法、手段而不是目的、目标。

利與弊

ok,讓我們回到mvp架構上。剛剛我們了解了什麼是mvp,讨論了mvp以及其它熱門架構,并且介紹了mvc,mvp和mvvm三者間的不同。這是關于mvp架構利與弊的總結:

**利

可測試(tdd)

可維護(代碼複用)

容易reviewe

資訊隐蔽

**弊

備援的,尤其是小型app開發

(有可能)額外的學習曲線

開始編寫代碼之前需要時間成本(但是我敢打賭,設計架構是所有項目開發所必需的)

show me the code!!!

這裡僅展示了mvp模式的一小段結構。如果你想了解更多項目或生動的代碼示例,請參考文章末尾的“連結和資源”。那裡有非常豐富和設計巧妙的示例,基本都托管在github上,以便你能clone,在裝置上運作,并了解工作原理。

首先,為每一個view定義接口。

/** 

 * interface classes for the top view 

 */ 

public interface topview { 

    /** 

     * initialize the view. 

     *  

     * e.g. the facade-pattern method for handling all actionbar settings 

     */ 

    void initviews(); 

     * open {<a href="http://www.jobbole.com/members/57845349">@link</a> datepickerdialog} 

    void opendatepickerdialog(); 

     * start listactivity 

    void startlistactivity(); 

}  

讓我們重寫topview類,要點如下:

topactivity隻是負責處理事件監聽或者展示每個視圖元件

所有的業務邏輯必須委托給presenter類

在mvp中,view和presenter是一 一對應的(在mvvm中是一對多的) 

public class topactivity extends activity implements topview { 

  // here we use butterknife to inject views 

  /** 

   * calendar title 

   */ 

  @bind(r.id.calendar_title) 

  textview mcalendartitle; 

  private toppresenter mtoppresenter; 

  @override 

  protected void oncreate(bundle savedinstancestate) { 

      super.oncreate(savedinstancestate); 

      setcontentview(r.layout.activity_top); 

      butterknife.bind(this); 

      // save toppresenter instance in a meber variable field 

      mtoppresenter = new toppresenter(); 

      mtoppresenter.oncreate(this); 

  } 

  /* 

   * overrides method from the {<a href="http://www.jobbole.com/members/57845349">@link</a> topview} interfaces 

  public void initviews() { 

      // actionbar settins 

      // set event listeners 

  public void opendatepickerdialog() { 

      datepickerfragment.newinstance().show(getsupportfragmentmanager(), 

              datepickerfragment.tag); 

      // do not write logic here... all logic must be passed to the presenter 

      mtoppresenter.updatecalendardate(); 

  public void startlistactivity() { 

      startactivity(new intent(this, listactivity.class)); 

這是presenter類,最重要的一點是presenter僅僅是連接配接view與model的适配橋梁。比如,topusecase#savecalendardate()是對toppresenter細節隐藏的,同樣對topview也是如此。你不需要關心資料結構,也不需要關心業務邏輯是如何工作的。是以你可以對topusecase執行單元測試,因為業務邏輯與視圖層是分離的。

public class toppresenter { 

    @nullable 

    private topview mview; 

    private topusecase musecase; 

    public toppresenter() { 

      musecase = new topusecase(); 

    } 

    public void oncreate(@nonnull topview topview) { 

        mview = topview; 

        // here you call view's implemented methods 

        mview.initviews(); 

    public void updatecalendardate() { 

        // do not forget to return if view instances is null 

        if (mview == null) { 

            return; 

        } 

        // here logic comes 

        string datetodisplay = musecase.getdatetodisplay(mcontext.getresources()); 

        mview.updatecalendardate(datetodisplay); 

        // here you save date, and this logic is hidden in usecase class 

        musecase.savecalendardate(); 

當然,盡管業務邏輯被實作在activity類中,你依然可以執行單元測試,隻不過這會耗費很多時間,而且有些複雜。可能需要更多的時間來運作app,相反,你本應該充分利用測試類庫的性能,如robolectric。

總結

這裡沒有萬能藥,而且mvp也僅僅是解決方案之一,它可以與其他方法協同使用,同樣,也可以有選擇的用于不同項目。

本文作者:佚名

來源:51cto