對MVP、Flux和RxAndroid框架的理解和選擇
為什麼要使用框架
使用框架,是為了提高生產效率。
什麼是生產效率?
上學的時候,導師曾說:我以前上學那會兒,什麼都得自己寫,別的不說,光把介面畫出來就夠發表篇論文了,哪像你們現在,拿個滑鼠拖幾下就能弄出介面來。
這就是生產效率。
為什麼使用框架能提高生產效率?
做過軟體的人都知道,我們開發一個軟體,其實就是把現實中的業務對映到軟體中去,這是一個對現實去做軟體建模,去做程式碼實現的過程,而現實又是極度複雜的,做工程的基本常識是,越複雜的東西風險就越高,所以軟體開發是充滿未知風險的,面對未知風險,我們是茫然且恐懼的,在茫然之下,我們最直接的反應是:這種事一般要怎樣做,有什麼經驗,有沒有什麼套路。
框架就是這樣一種套路,因為它已經通過某種正規化,完成了對業務的解析、對映和分層,在充滿未知的軟體開發中,框架的存在使開發有一定規矩可循,使常見的問題容易得到解決,使開發人員更專注於具體業務。
一般來說,使用框架有這樣幾點好處:
1、加快開發速度。很多框架會幫你實現一些通用的、偏底層的實現、例如用IDE繪製軟體介面、用Hibernate讀寫資料庫、用EventBus傳遞事件、用HttpClient處理網路請求等(Android開發的框架會更多一些),除非是特殊的環境或有特別的訴求,否則沒有開發者願意花費大量的時間和精力,自己再造一遍輪子。
2、降低開發風險。還是造輪子的問題,每個輪子都會有缺陷,但是大家都在用的輪子,相對更加可靠,發現缺陷也容易及時得到修復。
3、方便團隊協作。一般情況下,軟體開發都是團隊行為,團隊開發就要求在成員之間協調一致地並行工作,這就要求介面一致、風格一致等,這會帶來很多管理上的問題,而使用框架能較好地輔助這一點。
4、框架本身的優勢。每個框架的出現都是為了解決某些問題,像我們今天要討論的MVP、Flux和RxAndroid,都是為了解決日益複雜的業務邏輯導致軟體不可控的問題,MVP的思路是“挪”,在MVC的基礎上把業務邏輯從View中挪走;Flux的思路是“單向流”,用嚴格的單向資料流去實現比較容易跟蹤檢測的邏輯結構;RxAndroid的思路則是“鏈式邏輯”,用函式反應式程式設計的思想把邏輯、程式碼和執行緒統統簡化為一條鏈式呼叫。
MVP
關於MVP的經典表述可以參考Google開源專案Android Architecture Blueprints。
MVP想解決的問題是,Google原來為Android準備的MVC分層架構中,C的存在極其薄弱,存在嚴重的Activity膨脹的問題,在很多開發中,Activity既是顯示介面,又是業務邏輯,從一開始就沒有分散職能,Activity會隨著業務的變化急速膨脹,邏輯膨脹,程式碼膨脹,檔案膨脹,長期連續迭代下來,一個Activity的程式碼很容易拖到幾千行甚至上萬行,程式碼的複用性、可讀性和可維護性都變得非常糟糕。
這種情況下,一個比較合理的思路就是“挪”,把業務邏輯從Activity中挪出來,挪到Presenter中去,讓Activity迴歸View的角色,從此Presenter專注於業務,View專注於顯示。業務邏輯不再受Activity生命週期的影響,Activity也擺脫了業務邏輯無法複用的囧境。
“挪”業務邏輯的MVP,基本解決了Activity膨脹的問題,View層變得相對簡單而安全了,而Presenter層中的業務邏輯,也比較容易做單元測試,做程式碼複用,做進一步的抽象和分離。
但是MVP中的業務邏輯仍然複雜,而且邏輯流和資料流基本處於只要不出bug,就可以放任自流的狀態,體現在業務邏輯上就是無數錯綜交叉的業務線,體現在業務資料(Model)上就是無數的呼叫入口,長期運轉下來,會發現越複雜的業務就越難以追蹤邏輯運轉和資料狀態,對軟體的維護擴充套件非常不利。
也就是說,MVP需要一個狀態管理工具,用來督促開發者按照一定規律處理狀態的讀寫,避免程式碼變成一團亂麻。
Flux
關於Flux的經典內容可以參考Facebook的移動架構Android Flux
Flux試圖用“單向流”去實現狀態管理,它是一個單向繫結的單向資料流結構,一般我們看到的圖是這樣子的:
Flux是一個單向環,它實現業務的基本流程如下:
1、View響應操作並建立對應的Action
2、Action呼叫單例的Dispatcher去統一處理所有請求
3、Dispatcher持有所有Store物件,通過回撥讓Store處理資料
4、Store處理資料後,發出事件(EventBus)通知View更新解密
5、View接收Store的事件後,重新整理頁面
上面的圖並不能表現Dispatcher的中心作用,我們可以看這個圖:
可以看到,Flux實際上比MVP更囉嗦,要建立更多的物件,使用更多的訂閱和回撥,依賴EventBus去重新整理頁面...
但是,Flux能把所有的業務都引流到一個Dispatcher單例中處理,這實際上建立了一種機制,這種機制能在同一個地方查詢狀態、改變狀態、傳播狀態的變化,這就是MVP缺少的那種狀態管理工具。
在這種機制下,所有的業務都是從某處開始並進入Dispatcher,再離開Dispatcher去往某處結束,中間不可能有迴圈,每條業務邏輯都只朝著終點方向執行,這就實現了Flux“單向流”的思想,這就是單向資料流。
單向資料流對軟體開發來說,意味著什麼呢:
如上圖所示,單向資料流,意味著業務邏輯得到了有效的管理,我們可以很清楚地知道每條業務線上會發生什麼,不會發生什麼,軟體的狀態和行為變得容易預測,程式碼的可讀性和可預測性得到提高,業務流程得以簡化,容易排除錯誤,這給開發和維護帶來了極大的便利。
Flux當然也有問題,他太囉嗦,程式碼複用太差,會有嚴重的action類膨脹問題。感興趣的開發者可以再去了解一下相對簡潔許多的Redux,目前主要在web開發中和React一起使用,對Redux的介紹可以參考Redux中文文件
RxAndroid
RxAndroid其實就是在Android中使用RxJava,詳細內容可以參考給 Android 開發者的 RxJava 詳解
RxJava 是個了不起的工具,它使程式碼的編寫和維護更加容易,而且更少出錯,它的思路和MVP/Flux完全不同,如果說MVP/Flux關注的是類的分層和分工,那麼RxJava關注的就是純粹的業務邏輯鏈條。
RxJava通過訂閱事件驅動的鏈式邏輯呼叫,把複雜的非同步邏輯維持成為一條簡單的鏈式結構,來保持邏輯和程式碼上的簡潔,這也使業界普遍認為Rx程式設計屬於“函式響應式程式設計(FRP)”,因為它好像不再依賴於通過物件導向(OO)的設計來解決問題,而是通過 “鏈式”的思維方式,實現基於訂閱事件驅動的簡潔的鏈式呼叫,訂閱事件驅動的基本思想如下:
用被觀察者去發出事件,在觀察者中響應事件,形成異常簡潔的邏輯鏈條,這種簡潔即是RxJava的魅力和優勢所在。
一段RxJava的典型程式碼是這樣的:
我們看到,所有的事情都在同一個鏈式呼叫裡完成了,其中的訂閱和事件我們都不陌生,這也不是它保持簡潔的祕訣,RxJava保持簡潔的祕訣在於鏈式呼叫,而鏈式呼叫的祕訣在於,每次邏輯呼叫後返回的物件,都是自己或自己的同類,幷包含了繼續鏈式呼叫的方法。
通過把複雜的業務維持成一條簡單的鏈式結構,保持邏輯和程式碼上的簡潔,這種做法的好處是顯而易見的,簡潔、優雅、高效,而且現實中的業務線幾乎可以照葫蘆畫瓢地對映為程式碼中的邏輯鏈。
當然RxJava也有他的問題,就是對程式碼的掌控變弱了。能把複雜問題用簡單形式表達出來當然很了不起,但是複雜邏輯被表達為簡單鏈條後,一方面擺脫了臃腫的程式碼,另一方面卻因為程式碼邏輯過於緊湊,給排查故障帶來了障礙,比如說新手常犯的一個錯誤是,忘記將網路請求分配到非同步執行緒去工作,而預設搭配的Retrofit網路框架是無法在Android主執行緒上工作的,整條業務鏈從一開始就中斷了,但開發者能看到的直接反饋是業務鏈的結尾沒有輸出,很難精確定位到故障點,只好對整條鏈路進行檢查,這對開發和維護也是不利的。
感興趣的開發者也可以去閱讀我以前寫的文章;RxAndroid使用初探—簡潔、優雅、高效
如何選擇
在框架的選擇上,沒有最好的框架,只有最合適的框架,特別是很多框架只是一種程式設計思想,你很難說自己在程式設計時會特意使用某種思想,又特意不使用某種思想,就像前些年熱炒的設計模式一樣,不要為了使用而使用,能恰到好處地需要解決你的問題,就是適合你的。
更詳細點說,使用框架的問題不僅在於如何把框架應用到你的專案中,也在於如何在不必要用框架地方保持清爽簡潔,比如如果你的頁面非常簡單,沒有很多互動,就不要使用Rx、Redux、Flux,甚至MVP也不需要,你就簡單地在一個Activity裡實現就好。
總的來說,合適的就是最好的,不要過度設計,保留在必要時重構程式碼的能力就好。畢竟,對於軟體工程來說,完備周全的設計往往起不到應有的作用,反而是快速交付,快速迭代的策略越來越被證明是一種低風險的模式,不過,這個話題就是另外一個範疇了。
附錄
Android高階技術大綱,以及系統進階視訊;
附錄一;Android高階技術大綱
附錄二;Android進階實戰技術視訊
獲取方式;
加Android進階群;701740775。即可前往免費領取。免費備註一下csdn
相關文章
- MVC,MVP 和 MVVM 模式如何選擇?MVCMVPMVVM模式
- 談談對MVC、MVP和MVVM的理解?MVCMVPMVVM
- Java反應式框架Reactor中的Mono和FluxJava框架ReactMonoUX
- 個人對於flux、redux及vuex的理解ReduxVue
- js選擇物件和jq選擇物件的區別JS物件
- 圖床的選擇和使用圖床
- 對AIDL和Binder的理解AI
- 對session和cookie的理解SessionCookie
- MVP框架的演化MVP框架
- Get 和 Post 方法的選擇和URL的設計
- 對VUE框架的理解Vue框架
- Redis RDB和AOF取捨和選擇Redis
- 選擇排序和快速排序排序
- 選擇排序的簡單理解排序
- [譯]讓我來幫你理解和選擇Flutter狀態管理方案Flutter
- 舉例說明你對相鄰兄弟選擇器的理解
- MVC、MVP和MVVM的區別MVCMVPMVVM
- redis持久化的取捨和選擇Redis持久化
- 唯一索引和普通索引的選擇索引
- Signal:更多前端框架的選擇前端框架
- 我對部落格的理解和看法
- 選擇排序和氣泡排序排序
- 【HTML】顏色和選擇器HTML
- Spark 模型選擇和調參Spark模型
- Maven 和 Gradle:選擇哪個?MavenGradle
- Kubernetes – 標籤和選擇器
- python和java該如何選擇?PythonJava
- html和css選擇器使用HTMLCSS
- pandas索引和選擇資料索引
- 對javascript中的call()和apply()的理解JavaScriptAPP
- 一文搞定:前端如何選擇Angular、React和Vue三大主流框架前端AngularReactVue框架
- MVC模式和MVP模式的區別MVC模式MVP
- SVM 的核函式選擇和調參函式
- 【技術人生】工程師面對新質生產力的思考和選擇工程師
- 雲終端和瘦客戶機的區別和選擇
- Spark和Hadoop之間的主要技術差異和選擇SparkHadoop
- 我對遞迴的理解和總結遞迴
- 手把手擼套框架-ORM框架的選擇框架ORM