我對移動端架構的思考

玉剛說發表於2018-07-11

本文由玉剛說寫作平臺提供寫作贊助

原作者:Mr.S

版權宣告:本文版權歸微信公眾號玉剛說所有,未經許可,不得以任何形式轉載

架構就像是一場進化史,根據不同時期的需求,演變出不同的架構,車輪滾滾,到今天,移動端框架百花齊放,讓人目不暇接。但是其中的本質是磨滅不了的,換言之根本沒有磨滅而是隱藏到了人們所看不到的地方,但是依舊發揮著不可或缺的作用。

為什麼需要架構?

  • 一個Activity走天下,包含各種模組和功能
  • 臃腫類太多
  • 不同功能,不同層次拎不清楚,混合在一起
  • 重複程式碼太多,複用性為差
  • 無法協作開發,
  • 耦合嚴重,bug太多 等等

當我們新進一個公司,接受別人的專案的時候,基本都會說句MMP,SHI一樣的程式碼啊,啥玩意啊,搞得什麼啊。

我擦,我就改了一個引數,怎麼全亂套了,一個功能怎麼天上地下都需要改啊。

沒有經受過痛苦的人,是不會想了解架構的,只有痛苦過的人,才會急切的瞭解架構,好好做人,不作孽。

進化1.0 MVC

人們剛接觸程式設計,相信第一個遇到的框架就是MVC,不管你經意還是不經意寫出來的Android程式他就是MVC框架,只不過是MVC框架的某一個變種(最混亂的那種)。Android系統本身就根據MVC建造的。

  • XML的View層
  • Activity/Fragment的Controller層
  • 資料Model層。

我們編寫程式剛開始應該是這種結構

image.png

View層和Controller交織在一起。在Activity裡面處理各種事件和邏輯,介面的顯示和更新都在這裡,除了按功能分出去的模組外,能在Activity裡面寫的都會堆在這裡面,程式碼常常達到上千行,別說別人,自己再回到頭看的時候也是頭皮發發麻。

所以我們開始試著學Android系統的MVC來梳理程式碼。當然每個人從眾理解的也不大一樣。 不管是單向依賴還是雙向依賴都是為了把處理的職責交給Controller,這一點是不變的,所以不管你用的是哪一種形式都是不打緊的,沒有對與錯。根據自己的需求選擇最適合的一種依賴關係。

image.png

優點:

由於MVC很好的分離了檢視層和業務層,所以它具有以下優點

  • 耦合性低
  • 開發速度快
  • 可維護性高
  • 易於理解
缺點:
  • 由於MVC的設計思想是從Model出發,而沒有考慮到View端的複雜性,這樣導致的問題是Model難以符合複雜多變的View端變化。導致Model的作用很小,而很多View層的職責也轉移到了Controller層。Controller變得臃腫不堪。耦合性也變高了。
  • 測試困難

MVC使用的誤區

其實我在剛開始程式設計的時候,

誤區一:一度認為Model就是實體類(Entity)

正解:是MVC的Model應該有兩個功能:

  • 處理業務邏輯
  • 提供View顯示的資料
誤區二:把業務邏輯全部放在Controller端

正解:Model也會處理業務邏輯

這兩個誤區其實也和MVC的構成有關係,很容易讓人誤解,但是本質上還是不理解MVC,特別是對Model的作用不明白。

其實Model在MVC框架裡面的作用不僅僅是Entry,Model在MVC中起著很重要的作用,它更應該是業務邏輯的真正實現層,而Controller層更應該作為一個橋接的作用,把View的求情轉發給Model,再把Model處理結束的訊息告訴View。這樣做也就是介面和程式碼(邏輯)分離。

進化2.0 MVP

但是踏入誤區的人太多了,大家都在Activity裡面處理邏輯因為太方便了(寫的很爽)。 但是這樣之後,我們Activity的職責太多了,耦合也嚴重,所以我們就想著怎麼能給Activity減負,同時把耦合也降下來,所以就想找個哥們來替Activity分擔的責任,大家最後都只有一個責任,各層關係也相對好理解,大家照著這個方式寫,那多好,不用加班,天天都很快樂。

既然Activty這麼願意和View搞到一起(解耦太高了),那麼就讓他們倆在一起(在一起,在一起,在一起),共同負責View,我們在招聘一個職業經理人(Presenter)來處理事務,有啥事都找Presenter,你看著多方便啊,MVP是一個真正意義上的隔離View的細節和複雜性的模式

image.png

至於虛線的部分,這個和樓上的MVC的虛線一樣,每個人的理解不同,情況不同,選擇合適的就好,不用糾結誰對對錯,本質不變就可以的。

這樣就簡單多了,Activity和View兩人各種膩歪秀恩愛,各層的關係也一目瞭然,大家各幹各的事情,想要什麼和Presenter說一聲,有啥事情Presenter也會彙報,簡直爽的不要不要的。Presenter也會處理和View層的互動,Presenter在手天下我有。

優點:

1、模型與檢視完全分離(就像牛郎和和織女隔了一個銀河,每次都需要靠燕子來傳信,好可憐~~)

2、可以更高效地使用模型,因為所有的互動都發生在一個地方——Presenter內部

3、(Presener的複用)一個Presener可以用於多個檢視(View),而不需要改變Presenter的邏輯。檢視(View)的變化比模型(Model)的變化更頻繁的多 ,所以這樣超級方便。

4、(View的複用)View可以進行元件化。在MVP當中,View不依賴Model。這樣就可以讓View從特定的業務場景中脫離出來,可以說View可以做到對業務邏輯完全無知。它只需要提供一系列介面提供給上層操作。這樣就可以做高度可複用的View元件。

5、更容易單元測試

缺點:

1、由於對檢視的渲染放在了Presenter中,所以檢視View和Presenter的互動會過於頻繁。特別是需要修改檢視的時候,Presenter也需要跟著修改,很麻煩。

2、Presenter中除了業務邏輯以外,還有大量的View->Model,Model->View的手動同步邏輯,造成Presenter比較笨重,維護起來會比較困難。

3、其實總的來說就是結構很清晰,業務邏輯也很明白,耦合低,但是就是自己寫的麻煩,Presenter不好維護,工作量太大,太笨重,有點像MVC中的Activity了,職責太多了。也可以把這三個缺點總結成一句話,麻煩,尾大不掉。

進化3.0 MVVM

但是吧,你們爽了Presenter可就累壞了,大事小事都要經過Presenter,就連放個屁也要Presenter來扒褲子,日子過得久了,Presenter總會有意見啊,不帶這麼使喚人的。

我們就想啊,以前過多的依賴Activity造成結構混亂,耦合太高,後來有了Presenter,雖然耦合大大降低,但是還是過於依賴Presenter,為啥總會過於依賴某一個東西呢,就不能大家都乾點活,耦合性也不高的嗎?就不能大家有事相互通知嗎?非要那麼懶依賴別人嗎?總要有點正能量吧。那麼我們就把Presenter辭退,引入了一個新的小夥伴VM(ViewModel即 Model of View)它即包含了Modle也有View的狀態。

MVVM模式中,一個ViewModel和一個View匹配,它沒有MVP中的IView介面,而是完全的和View繫結,所有View中的修改變化,都會自動更新到ViewModel中,同時ViewModel的任何變化也會自動同步到View上顯示。

這種自動同步的原因其實就是是ViewModel中的屬性都實現了observable這樣的介面,也就是說當使用屬性的set的方法,都會同時觸發屬性修改的事件,使繫結的UI自動重新整理。

在android中DataBinding幫助我們實現MVVM,在XML進行資料繫結,增加了XML的重量,不再像以前那樣僅僅是佈局,均衡了各部分的職責。

所以MVVM比MVP更升級一步,在MVP中,V是介面IView, 解決對於介面UI的耦合; 而MVVM乾脆直接使用ViewModel和UI無縫結合, ViewModel直接就能代表UI.

image.png

優點:

1、解決了MVP大量的手動View和Model同步的問題,提供雙向繫結機制。提高了程式碼的可維護性。大大方便了開發者。(只有試過才知道有多方便)

2、簡化測試。

3、響應式程式設計更方便。

缺點:

1、過於簡單的圖形介面顯得大材小用(讓開發者麻煩的都是缺點)

2、檢視狀態較多,ViewModel的構建和維護的成本都會比較高。

3、資料繫結的宣告是指令式地寫在View的模版當中的,這些內容是沒辦法去打斷點debug的。(這是很碉堡的,只能自己細心了)

進化4.0 CLEAN

進化了一代有一點,作為東家的谷歌坐不住了,給不給我面子了,來來來,我給你們一個終結的,Clean? WTF?這個洋蔥一個的傢伙是誰?再給你一個圈就是五環了~~四個圈圈了不起啊?

image.png

官方的話說來就是:獨立

  • 獨立於框架。該體系結構不依賴於某些特徵庫軟體庫的存在。這允許您將此類框架用作工具,而不必將您的系統塞入其有限的約束中。 可測試。可以在沒有UI,資料庫,Web伺服器或任何其他外部元素的情況下測試業務規則。
  • 獨立於UI。UI可以輕鬆更改,而無需更改系統的其餘部分。例如,可以使用控制檯UI替換Web UI,而無需更改業務規則。
  • 獨立於資料庫。您可以換掉Oracle或SQL Server,用於Mongo,BigTable,CouchDB或其他東西。您的業​​務規則未繫結到資料庫。
  • 獨立於任何外部機構。事實上,您的業務規則根本不瞭解外部世界。
依賴規則:

同心圓代表軟體的不同領域。一般來說,你走的越遠,軟體就越高。外圈是機制。內圈是政策。

使這種架構工作的首要規則是依賴規則。此規則表明原始碼依賴項只能指向內部。內圈中沒有任何東西可以對外圈中的某些東西一無所知。特別是,內圈中的程式碼不得提及在外圈中宣告的內容的名稱。這包括功能,類。變數或任何其他命名的軟體實體。外圈中使用的資料格式不應該被內圈使用,特別是如果這些格式是由外圈中的框架生成的話。我們不希望外圈中的任何東西影響內圈。

官方的話就是繞,其實就是依賴是能向裡依賴,白話就是內圈向外圈提供服務,比如你去食堂吃飯,選擇不同的視窗,對著裡面喊到給我來一份魚香肉絲。你不用管裡面的師傅是不是昨天那一個,也不用管裡面的菜是不是昨天的那個口味,你只管你的目的是來一份魚香肉絲就可以了,裡面的菜和師傅,也不用管你是誰,是不是個漂亮的MM,他們只管自己事情,對他們圈外的不依賴,反正你依賴我就可以了。

image.png

Clean將核心業務(Domain層)、UI相關(Presenter層)以及資料載入(Data層)彼此獨立開來,不同的層之間由介面依次連線起來,但卻又彼此不瞭解彼此的具體實現。

優點:

1、程式碼複用性更高 2、更易於測試 3、耦合度更小

缺點:

1、學習難度大(容易懵逼) 2、程式碼量大(一個小Demo都夠你寫一壺了)

從MVC->MVP->MVVM->MVP-Clean 後者解決了前者遺留的問題,把前者的缺點優化成了優點。一代更比一代強,但是都是需要代價的,學習成本更高,更加規範,但是並不一定就誰比誰更好,只有誰更適合你的專案,都有優劣,怎麼選擇需要靠自己了。 其實MVX系列的本質一直未變:

  • 關注點分離原則
  • 高內聚低耦合原則

同時也希望適度設計,不要太過追求某一點。會得不償失。找到最經濟的方案才是王道,也可以多種方案配合一起配合。

image.png

如何能夠更好的將多種架構表現在一款APP裡面呢?對!元件化。

元件化

image.png

元件化開發就是將一個app分成多個模組,每個模組都是一個元件(Module),開發的過程中我們可以讓這些元件相互依賴或者單獨除錯部分元件等,但是最終釋出的時候是將這些元件合併統一成一個apk,這就是元件化開發。

不同的Module我們根據職責劃分選擇不同的架構,可以最經濟的開發一款APP。

image.png

目前成熟的方案有阿里的ARouter,通過路由的方式進行通訊,感興趣的同學可以學習下。

優點

1、低耦合 2、可以針對測試(降低測試成本) 3、複用性強 4、支援並行開發 5、適合使用多程式 6、減小編譯時間

缺點

1、不適合小型應用或者獨立開發 2、各元件開發人員聯調,開發進度不一致等問題

應用場景

假如一個app使用者量很大,業務豐富,比如某寶,很明顯不是一個小團隊可以開發的了的,如果不分模組的話,工程的複雜度就太大了,編譯時間估計半天都完不成,維護更不用說了,簡直不是人乾的了。所以很適合分模組來開發。各個模組的耦合性也能降到最低,不至於一個模組有問題,牽連別的模組。編譯時間也會大大減小。每個模組相當於一個小APP,更容易靈活的編譯,編寫。測試也只需要測試本模組,最後聯調整體就可以了。所以說業務越複雜,元件化越有優勢。

小結

移動端架構的差異化體現在通訊機制上。通訊機制主要分為3種: 1) 物件持有 2) 介面持有 3) 路由

物件持有最簡單,但是解耦率最低; 介面持有較為複雜,實現解耦的需求,但是解耦不徹底,相互持有互動方的介面; 路由機制可以實現完全解耦,實現元件化。

完了嗎?不不不~~移動端不僅僅Android和iOS,還有前端,上面僅僅是MVX的進化史,同時還有不同分支的進化。

當下移動端的趨勢,越來越向大前端靠攏了,Android的道友們也越來越瑟瑟發抖,看到RN,Flutter等等的興起,越來越覺得僅僅學習Android是不夠的,還要學習跨平臺,大前端的知識。俗話說他山之石可以攻玉,我們對架構的思考也是如此,不能僅限於自己的一畝三分地,也要學習下友軍的套路。

這類一般都是響應式檢視的系統,MVVM就是個響應式,所以其實都是有聯絡的。

進化 5.0 Flutter/ReatNative

  • Flutter 是Google用以幫助開發者在Ios和Android兩個平臺開發高質量原生應用的全新移動UI框架,也是據說要把Android替換掉的一個恐怖存在(目前現在還是個弱雞)

  • ReactNative 讓開發者使用 JavaScript 和 React 編寫應用,利用相同的核心程式碼就可以建立 Web,iOS 和 Android 平臺的原生應用。React Native 的宗旨是,學習一次,高效編寫跨平臺原生應用。

ReatNative

JS 開發者可以用類似DOM程式設計模型就可以開發原生APP,畫UI只需要畫到virtual DOM 中,不需要特別關心具體的平臺, RN 會把應用的JS程式碼編譯成一個js檔案,RN的整體框架目標就是為了解釋執行這個js 指令碼檔案,通過bridge 傳遞到native , 然後根據資料屬性設定各個對應的真實native的View。

image.png

RN官方給的框架是Flux:

image.png

  • action 封裝請求
  • dispatcher 註冊處理器、分發請求
  • store 是處理器,處理業務邏輯,儲存資料
  • view 根據store提供的資料進行展現;接受使用者的輸入併發出action請求。

解耦很細,做一個功能會涉及很多地方,所以人們對於這個官方架構進行簡化,於是有了Reflux。

Reflux:

Reflux.png
這個三層框架就大大簡化了flux的複雜互動。不管是物件導向還是面向狀態,其實本質上是一致的。三層框架能夠很好處理檢視,資料,事件,邏輯的問題。

我們也可以參考另外一種架構Redux。

Redux:

Redux.png
我們可以看出 其實也是分為三層,元件,Store,Action。元件繫結這Action和state,store reducer都是頁面單例,易於管理。action為請求dto物件,是請求型別,請求資料的載體,reducer是處理請求的方法。不允許有狀態,必須是純方法。必須嚴格遵守輸入輸出,中間不允許有非同步呼叫。不允許對state直接進行修改,要想修改必須返回新物件。

這就是面向狀態程式設計

react的元件只關注資料的最終狀態,資料是怎麼產生的並不關心。這個是RN的核心思想。

  • 優點 1、提升產品迭代速度,APP迭代週期變短 2、減少研發成本(可不,ios和android都可以下崗了) 3、提升開發測試效率(不用雙端兩套程式碼分開測試了)

  • 缺點 1、原生有些功能實現不了或者說很複雜 2、需要掌握IOS和Android的知識,個人學習成本高 3、需要相容IOS和Android(很苦逼)

Flutter

今年穀歌推出了Flutter的Beta版本,離正式的出生越來越近了。我們來看看這個新同學吧。

Flutter主要解決了移動開發中的兩個重要問題,一是原生應用程式的效能與平臺的整合;二是提供多平臺、可移植的UI工具包支援高效應用開發。

和RN的跨平臺思想不同

RN是將一種設計理念延伸到兩個平臺,而Flutter則實現了一套程式碼,部署多個平臺。解決了RN開發需要IOS和Android基礎的窘迫和成本。在Flutter中,每個應用程式都是Widget,這點和其他的應用框架不一樣,Flutter的物件模型是統一的,也就是控制元件。

Flutter 基本上就是一個 V(View),響應式檢視,它可以是無狀態或有狀態的 widget,就連 AppCompatActivity 也是一個 widget。是不是很過分?簡單到粗暴。採用widget 和 state分離 ,比如我們在原生裡大量重複程式碼來管理 Activity 的生命週期。但是在 Flutter 中,這是不需要的,從設計的角度就解決了這個問題。就像LifeCycles也是這種思想,解決了生命週期的問題。

對於這種面向狀態程式設計的思想,架構和RN是類似,同時我們也期待Google的官方架構指導的出現,會不會有更多的驚喜呢?

image.png
優點 1、響應式檢視,不需要JavaScript的橋接器 2、效能更好,相容性更好 3、程式碼將AOT編譯為本機(ARM)程式碼 4、美觀,可定製的UI元件,開發人員完全控制UI元件和佈局 5、熱重新載入

缺點 1、包體很大需要打包自己的SDK 2、佔用記憶體空間大 3、啟動時間很慢 4、流暢性和原生有差距

不過因為FLutter還沒與正式出版,相信FLutter會解決這些缺點的。

RN和Flutter差別:

對於這兩者系統架構的區別 這裡有兩張圖:

Flutter.png
ReatNative.png
我們可以看出來 主要差別就是Widgets的差別,還有Bridge中間層的區別,Flutter從理論上的速度要別RN高很多,沒有了中間層的倒轉,有自己的Widgets可以保證IOS和android上的統一。

實現跨平臺的這兩種方式也是架構設計的體現,Bridge有沒有和Presenter很相似,連線著js和系統。RN是將一種設計理念延伸到兩個平臺,而Flutter則實現了一套程式碼,部署多個平臺。

不同的設計思想,會走向不同的路,到底是哪一種走的更好,這個可能就需要隨著時間去檢驗了,雖然理論上FLutter要比RN好很多,但是現在的顯示很殘酷。不過FLutter才是Beta版本,潛力非常大。

image.png

寫在最後

本文把目前移動端主流的架構順了一遍,也是我自己對架構認識的一個時間軸,從剛開始的胡亂編寫,到後來有些章法,不過還是對框架的領悟很淺,肯定有很多謬論錯輪,希望以我個人對架構的思考可以拋磚引玉,如果可以給你一些幫助,那是極好的一件事情了。

不管是RN還是Flutter,亦或是原生的IOS和Android,其實他們本質程式設計思想是一致,同樣,隨著更多更方便的框架出來,我們的理解能力和學習能力受到了很大的挑戰,很多同學都說學不動,但是事實上技術的更新換代遠比我們學習新知識要快,往往我們剛學會這個,又會出了另外一個,我們只要掌握了它的核心思想,就不會那麼累和無奈了。

這就是技和道的差別,通過技最終掌握道,而不是以技學技,那樣沒有終點。

我們在實際中如何選擇架構,其實這個的因素很多,最關鍵的是一個經濟,比如專案時間比較短,還是獨立開發,我們可能就要用MVC了,或者說應用的功能相對簡單,也會取用MVC,至於MVP,或者MVVM,CLEAN,這個對於大型開發,特別是協作開發,有著很大的幫助。因為我們工作不僅僅是技術,還有專案週期,老闆的喜好,歷史遺留問題,所以選擇最經濟的一個,而不是選擇那個公認最好的那個。

移動端的趨勢越來越走向統一了,我們發現越來越多的公司,需要大前端的人才,所以要一直學習,創造時代的人都沒有說累,我們跟著學習的人更不應該喊累,沒有學不動,只有不想學。同時歡迎大家來剛哥的知識星球,一起激勵,一起砥礪前行。

參考:

React Native執行原理解析 https://blog.csdn.net/xiangzhihong8/article/details/52623852

React Native vs. Flutter 評測 https://mp.weixin.qq.com/s/Or28Gf71wuJrCUP0lKGpGg

ReactNative的元件架構設計 http://react-china.org/t/reactnative/3486

為安卓開發者介紹的移動開發框架 Flutter https://blog.csdn.net/Px01Ih8/article/details/79683595

我對移動端架構的思考
歡迎關注我的微信公眾號,接收第一手技術乾貨

相關文章