首頁»移動開發»說說Android的MVP模式

說說Android的MVP模式

來源:toughcoder.net 發布時間:2015-12-15 閱讀次數:

  安卓應用開發是一個看似容易,實則很難的一門苦活兒。上手容易,看幾天Java,看看四大組件咋用,就能整出個不太難看的頁面來。但是想要做好,卻是很難。系統框架和系統組件封裝了很多東西,開發者弄幾個Activity,用LinearLayout把布局組合在一起,添加點事件監聽,一個應用就成型了。紅海競爭,不管多么復雜的UX和業務邏輯都是一個月快速上線,二周一個迭代,領導和產品早上改需求,晚上改設計,再加上產品經理和設計師都按照iOS來設計,這一系列原因導致很多安卓應用不但體驗差,不穩定,性能低,而且內部代碼相當之混亂,即使BAT也是如此。

  反觀國外市場(谷歌應用市場)上面的大部分應用都還是比較好的,表現在符合安卓設計規范,性能和穩定上表現不俗,體驗上更符合安卓系統,而且會發現他們的代碼也是很有設計思想的。GitHub上面的很多安卓開源項目也都是源自國外的優秀開發者以及他們的項目。

  安卓應用也是軟件,代碼結構合理,層次清晰不但容易維護而且還容易做自動化測試和單元測試,這是開發者的美好愿望,也是提升效率的必然之路。

  安卓由于系統架構特性,UI組件Activity中融合了View的處理,事件處理和邏輯處理,隨著業務的越來越復雜,導致Activity也越來越雍腫,幾千行的Activity隨處可見,Fragment也不能解決問題,千行以上的Fragment也不在少數,這個時候就完全不要談什么可維護性,可測試性了。能完成需求就算高手了。

  MVP便應運而生,就來解決這些問題的。

 什么是MVP模式

  MVP是針對有GUI存在的應用程序,比如像安卓,像水果以及PC的客戶端軟件中用以劃分組織代碼的一種設計模式,是由MVC模式升級演進出來的,目的在于,對于GUI層來說,把UI展示與邏輯分開。

  • Model – 為UI層提供的數據,或者保存UI層傳下來的數據
  • View – 單純的展示數據,響應用戶操作并都轉發給Presenter來做具體的處理
  • Presenter – 邏輯控制層,從Model處取數據,運算和轉化,最后用View來展示;并處理View傳過來的用戶事件,并做處理

  需要注意的是MVP僅用于應用中的GUI部分,它并不是整個應用的架構方式。一個應用的主要的架構應該包括基礎組件,業務邏輯層和GUI展示層,而MVP僅是用于展示層的設計模式。另外,它是一個方法論的東西,沒有固定的實現方式,只要能體現出它的方法就可以算是MVP。

  雖然是方法論,但是也有一些指導性的原則來約束實現:

  • Model與View不能直接通信,只能通過Presenter
  • Presenter類似于中間人的角色進行協調和調度
  • Model和View是接口,Presenter持有的是一個Model接口和一個View接口
  • Model和View都應該是被動的,一切都由Presenter來主導
  • Model應該把與業務邏輯層的交互封裝掉,換句話說Presenter和View不應該知道業務邏輯層
  • View的邏輯應該盡可能的簡單,不應該有狀態。當事件發生時,調用Presenter來處理,并且不傳參數,Presenter處理時再調用View的方法來獲取。

  從這里可以看的出來,其實,MVP的目的就是把GUI的邏輯都集中在Presenter層,又把View層和Model與其用接口分離,讓View盡可能的簡單,這樣可以加強移植性。因為View層是肯定不能移植的,不同的平臺GUI的窗口部件肯定不一樣,Model也是不太好移植的,因為每個平臺的IO也都是不一樣的。但是,MVP中的P肯定是可以移植的,因為它里面只有邏輯,且View和Model都是接口,所以很容易移植。同時,因為View和Model都是接口,這個Presenter也非常好測試,只要實現一個View的接口和Model的接口,就可以單獨的測試Presenter了。

  嚴格來講,View只是被動的顯示,提供方法由Presenter來調用,數據等都是由Presenter來提供,內部不能任何的邏輯與狀態,邏輯和狀態都應該是在Presenter中。UI事件發生時,調用Presenter的方法來處理,不能傳參數,也不能有返回值,在Presenter中處理后再調用View來更新數據和狀態。

 MVP與MVC的區別

  MVC之中邏輯是放在了Model里,Controller負責橋接View和Model,View發生變化時通知Controller,Controller再通知Model,Model進行邏輯處理,更新數據,然后通知View來刷新。可以看到MVC中三者之間都有聯系,如果處理不好,或者當View比較復雜時,三者之間都會雙向關聯。MVC在命令行應用,以及WEB中有大量的應用,但對于客戶端(PC和移動端)的GUI應用,MVC往往解決不了復雜性,移植性上以及可測試性上也沒有優勢。

  MVP的改進在于:

  • 邏輯放在Presenter中
  • View和Model抽象成為接口

  這樣就帶了二個好處:

  • 代碼更加容易移植
  • 代碼更加容易加入Unit Testing

 如何在安卓中實踐MVP

  MVP是一個方法論的東西,也就是沒有任何固定的具體的實現形式,只要能夠把View跟Model解除聯系,把邏輯都放在Presenter中,那么就能算得上是MVP,一些具體的實踐的指導性原則:

  • View是一個接口,負責被動的把處理好的數據顯示出來
  • Model也是一個接口,負責獲取數據和存儲數據
  • View調用Presenter處理用戶事件也是一個接口,稱為事件Delegate
  • Presenter持有的是View的接口和Model接口

  安卓的Activity是一個比較奇葩的角色,在MVP中,既可以用作V,因為一個應用的根布局總是由Activity來創建的。當然也可以當作P,因為Activity是一個應用的入口,也是出口,再加上一些關鍵的系統事件也都是通過Activity的方法來通知的(比如configChange, saveInstance)。其實,都可以。因為MVP是方法論,并沒有固定的形式,只要是把數據處理的邏輯都封裝在Presenter里,讓其去控制View和Model,讓Activity來承擔View還是Presenter,其實都可以。

 MVP不是銀彈,僅是展示層的一種范式而已

  最重要的一點就是要明白,MVP不會拯救你的應用,不要以為使用了MVP就能讓代碼更容易維護,更少的Bug,添加新功能會更容易。MVP僅是GUI層的一種編程范式而已,且因為它是方法論的東西,對實現方式并沒有固定的形式,所以會被濫用,如果沒有深刻理解MVP的思想,更加會導致災難性的結果。

  軟件,移動應用也不例外,如果功能簡單,業務簡單,那么代碼怎么寫其實也都無大礙,但當功能越來越多,業務越來越復雜的時候,就必須要采取必要的方法來應對復雜度和軟件的可開發性,可維護性。比如,說的夸張一點,一個helloworld式的應用,你怎么寫都可以。但當功能復雜到一個Activity幾千行代碼的時候,你再怎么MVP,MVC或者MVVM都不能解決問題,再怎么把Activity當成P或者當成V都沒有用。

  要知道MVP僅是解決GUI應用程序中展示層的問題,并且它帶來的最大的好處是方便測試和移植,因為邏輯都在P里面,P持有的又僅是View和Model的接口,所以P是可測試的,Mock一個View的實現,和Mock一個Model的實現,就可以完全脫離平臺和框架的限制來自由的測試P。同樣,移到一個新的框架和平臺后,只需要實現View和Model就可以了,P是不需要改變的。

 分層和模塊化才是解決應用越來越復雜之道

  分層

  所謂分層,也就是應用程序的架構方法,把應用程序分成好多層,可以參考Bob大叔的The Clean Architecture

  至少應該分層三層,最底層是平臺適配層,把用到的平臺的組件,控件,工具,比如UI組件,數據庫等等,進行封裝;中間層就是業務層,就是你應用的核心的業務邏輯,或者說你的應用解決了用戶什么樣子的問題,這一層是不會隨著平臺和UI的改變而改變的。比如新聞閱讀類,那么從服務器拉取數據,解析數據,緩存數據,為上層提供數據這些事情都屬于業務層;最上面就是展示層或者叫做UI層。展示層是可以調用業務層的方法和數據。這樣分層,可以讓展示層只是負責與用戶交互,展示業務數據,展示層會變得簡單很多,同時業務層因為不涉及具體的平臺和UI的細節,就非常容易移植,當移植到新平臺或者要做UI改版也是非常容易做的。

  模塊化

  另外一個就是模塊化,其實這是軟件開發的一個非常基本的方法,也是非常有用的一個方法。模塊劃分的方法非常簡單就是按照功能來劃分。讓模塊處理好自己的事情,暴露統一的接口給外部,定義好輸入與輸出。輸入就以參數和方法形式暴露,輸出最好以Delegate方式,這樣能把耦合降到最低。再由一個統一的頂層類來管理各個模塊,頂層直接調用各模塊,各模塊通過Delegate方式來回調管理者。

  對于業務層,模塊化相對比較容易,因為這里并不涉及UI和平臺的特性,業務層都應該是獨立的,可移植的,全都是自己寫的類。

  但對于展示層,通常沒有那么的容易,因為有平臺的限制。比如說安卓,根布局必須由Activity來創建。首先,模塊的劃分也要以功能為界限。然后,就是Activity的布局,要把布局按功能區域來管理,然后把每個功能模塊的top container傳給模塊,具體內部如何布局,如何填充數據,就由模塊自己負責。Activity就起管理各個模塊的作用。再有,模塊間的通信,可以都通過Activity來,比如模塊1有模塊2的入口按扭,但是模塊1與模塊2之間沒有交集,這個時候的處理方式就是模塊1Delegate給Activity,然后Activity再調用模塊2來顯示和隱藏。如果模塊多到Activity的管理工作也變得龐大復雜時就要拆出子Controller來管理模塊,也就是三層,甚至還可以四層。模塊的原則就是做好封裝,讓外層管理變得簡單,這樣外層管理的復雜度就會降下來,就好比公司人員的組織架構一樣。

<LinearLayout>
  <LinearLayout id="module1" />
  <RelativeLayout id="module2" />
  <ListView id="module3" />
</LinearLayout>
public class DemoActivity extends Activity implements Module1Delegate, Module2Delegate {
  @Override
  public void onCreate(Bundle bundle) {
    setContentView(R.layout.demo_activity);
    Module1 module1 = new Module1(findViewById(R.id.module1), this);
    Module2 module2 = new Module2(findViewById(R.id.module2), this);
    Module3 module3 = new Module3(findViewById(R.id.module3));
    module1.render();
    module2.render();
  }

  @Override
  public void onModule1() {
    Log.e("Demo", "module1 say hello to the world.");
  }

  @Override
  public void onModule2(boolean show) {
    if (show) {
      module3.show();
    } else {
      module3.hide();
    }
  }

  其實,還可以做的更徹底一些,那就是Activity中的布局都由ViewStub來組裝,然后由各個子模塊來決定如何布局。

  對于多層全屏層疊的應用來說,要簡單一些,對于每一層都可以由Activity或者Fragment來實現,如果業務層已經抽離出來,就都可以直接調用業務層來獲取數據,因此也不會有傳遞數據的麻煩。

  做好了分層和模塊化,我相信,能解決絕大多數應用遇到的問題。至于模塊內部用什么MVP,MVC,MVVM,其實真的無大害,因為模塊內部的實現方式不影響其他模塊,也不影響外部管理和level更高的類。

 把基本的原則做到就夠了

  編程是一項社會活動,所以人和人與人之間的關系才是核心,優秀的人,你發現他也沒有用什么MVP,什么MVC,什么高大上的設計模式和算法,但是他的代碼是很清晰,很容易看懂。有些即使號稱用什么高大上的,最先進的設計模式,但是代碼仍是一坨坨的,可能連他自己都看不懂。

  把基本的抽象和封裝真正做到位了,就夠了,代碼水平可以的話,再能做到命名見名知義,小而活的方法,小而活的類,一個方法只做一件事,一個類只做一件事情。做到這些,也就夠了。

  至于什么高大上的MVP,什么XP,什么TDD,什么結對,其實都是浮云,如果你的水平比較高,代碼sense較高,那么用不用這些方法差別不大。

  MVP的核心目的是方便UT,因為把展示層的邏輯都集中在P,而P又不依賴于具體的View和Model,所以可以隨便Mock一個View和一個Model來測試P,甚至P可以獨立于平臺的限制來單獨的測試。所以,如果你不搞UT,以不以MVP方式來實現,其實沒啥影響,甚至網上不少人還專門為MVP而弄出幾個抽象的類,把Activity啥的封裝了一下,號稱MVP框架,毫無實用價值。軟件方法,切忌生搬硬套,一定要先理解透徹方法,再理解透徹你的問題和環境限制,然后靈活運動,什么叫理解透徹呢?就是你能給別人講明白時。這說起來還是太抽象,只能在實際運用中慢慢領悟。

  再有就是Unit Testing這玩意兒,實際的意義也沒有那么大,要知道寫測試代碼通常要比生產代碼花更多的精力,前提還是你的代碼寫的可測,可測性比可讀性還要難一點,說白了這對開發者水平的要求相當的高,不是看了一遍書,學習一下JUnit就能搞得好的。還有就是如果你的需求經常變動,移動互聯時代這是家常便飯,那么做UT會讓開發量double甚至tripple,因為之前寫的UT全沒有用。

  還想說一點就是,軟件開發方法這東西必須是由上向下推動,也就是由老板帶頭來推動,否則技術小組長或者開發者自己是很難推得動的。特別是像UT,Code Review或者結對之類的會“降低開發效率”的方法。這些方法短期內不會提升效率和質量,只會降低需求的產出率,平均開發水平比較高的團隊也至少要幾個月后才能真正的適應這些方法,然后才有可能提高效率和提高質量。如果不是老板主動推動,誰能受得了呢?KPI咋整?

 結論

  MVP或者MVVM帶來最大的好處是:

  • 方便移植
  • 方便UT

  另外,要注意MVP僅是展示層的方法論。應用整體還是要進行分層和模塊化。如果分層和模塊化進行的徹底,并且在移植和UT沒有強烈的需求,其實MVP與不P真的不重要。

 參考資源

QQ群:WEB開發者官方群(515171538),驗證消息:10000
微信群:加小編微信 849023636 邀請您加入,驗證消息:10000
提示:更多精彩內容關注微信公眾號:全棧開發者中心(fsder-com)
MVP
網友評論(共0條評論) 正在載入評論......
理智評論文明上網,拒絕惡意謾罵 發表評論 / 共0條評論
登錄會員中心
李逵劈鱼9900炮 天津时时彩开奖数据 福建时时必开号码 七乐彩走势图带机选 拼搏在线彩票网彩神通 陕西快乐十分实时开奖结果 内蒙古时时1019 温州麻将软胡硬胡 北京时时综合走势图 白小姐资讯手机看开奖 南粤36选7走势图开奖结果查询