移動客戶端架構案例分析與思考


移動客戶端架構案例分析與思考

寫在前面

關於題目

分享之前,想說一下為什么選擇了“架構”這個主題,其實初衷有兩個:

第一,“架構”對於我們來說實在是太重要了,咱們雖然沒有架構師這個職位,但是在開發的時候,都需要先有個很好的設計,希望我們的代碼是易維護的,而“設計”往往都會落到“架構”上。所以希望這次分享能夠對於大家在架構設計上有一點幫助。

第二,即便“架構”如此的重要,大家再聊到“架構”這個話題的時候,還是感覺到有點“虛”。想想原因,可能是因為每個人對於“架構”的理解可能都不太一樣,一個人在不同階段對於“架構”的理解也會不一樣,架構設計還很依賴於實踐和經驗,很多設計細節(取舍)都是在實踐中不停的迭代、改進,進而反思才能得到價值觀的升級。所以我借這次機會,也將我自己之前零散的架構方面的理解,總結一下,爭取能形成一點體系上的認識。希望大家聊起架構時,能稍微不那么“虛”。

分享形式

為了不那么枯燥,今天分享形式,我選了3個架構的案例,來進行分析,來試圖講清楚我對於架構的認識,以及怎么樣設計出一個好的架構,當然這個話題太大了,我今天先給大家開個頭,希望先能有一點感覺就好。

最后希望大家帶着批判的精神來聽,歡迎多交流。

第一個案例:獵戶語音iOS SDK —— 架構是被業務驅動的

第一個案例,舉一個我們自己的,放在第一個來講,也是想強調“懂業務”對於設計出好的架構來說,是非常重要的,這也是一些人往往會忽略的點。

很多同學肯定想,在項目的一開始,就設計一個完美的架構,以后能 hold 住各種變化。其實這是不可能的,架構一定是隨着業務的變化,而不停的演化和進化的,在有的階段還可能對於之前架構做比較大的調整。

背景

大家都知道,我們同時維護了幾個App,比如小豹、小雅、錘子等,這些上層App,都要依賴於底層的一些framework,比如

  • 業務中心 OrionServiceSDK.framework (包括很多主要業務)
  • 信息流 OVSChat.framework
  • 技能商店 OVSSkillStore.framework
  • 聲紋 OVSVoicePrint.framework
  • 等等

在早些的時候,我們對於 SDK 的拆分粒度比較細,比如一個“找手機”技能,都會做成一個 framework。當時的 framework,應該在20個左右,各個 framework(組件) 的依賴關系圖如下:

歷史原因

我們這么做當時的原因很簡單,希望能夠解耦各個組件,將組件盡量拆細,然后App方想使用什么功能都可以做到熱插拔,不多也不少,多好~

理想 VS 現實

理想和現實往往會出現矛盾,到實際應用的時候,這樣的設計就給我們帶來了問題。如果對每個 framework 進行編號,比如1到20,那么我們理想中是這樣的:

但是現實其實是這樣的:

對比兩張圖的含義就是,實現中我們各個App,使用的 framework 大體相同,我們即便把業務拆的很細,若干個被拆分的 framework 其實還總是綁在一起使用。並且,還給我們維護帶來了巨大的問題,光每次打Git tag,都要折騰一會,會感覺精力都花在了一起輔助工作上。

優化

針對這個問題,我們專門優化了 framework 的個數,將相似業務的 framework 進行了合並,最終 framework 的數量減少到了10個一下,組件之間的依賴圖變成這樣:

優化之后效果也很明顯,我們對於各個framework的維護,變得簡單多了。

兩個彩蛋

另外在優化的過程,有兩個值得一提的是:

  1. 之前“信息流“和”推送“還存在環狀依賴的問題,就會導致當你想把 ”推送“的framework合並到業務中心的時候,竟然還得讓業務中心去依賴信息流,這個當然是無法忍受的,解決辦法也比較經典,就是讓依賴下沉,把 Push 依賴的信息流的協議,放到了業務中心中。
  2. 這次的優化,其實是將”松散“變成了”耦合“,和我們平時常提到的觀點剛好相反,但是確實是我們當前甚至今后一段時間內做所以我想說的是,”耦合“其實只是一個特征,雖然大部分情況是缺陷的特征,但是當耦合成為需求的時候,耦合就不是缺陷了。(有沒有一點在哪里聽過的感覺)

小結

我們談”架構“的時候,說的最多的就是”取舍“,什么叫”取舍“,就是說你不能很簡單的就判別出哪個是好的,哪個是不好的,總是覺得有點左右為難。而如何取舍?業務就是非常重要的一個標桿,只有結合業務,才能判斷出哪個是最適合自己的。我們結合了業務,對自己的 framework 的數量進行了精簡,當然也可能會根據業務的變化,在未來某天,需要將現有的framework拆分的更細。

第二個案例:餓了么移動APP的架構演進 —— 形成體系的認識

你做的項目,技術架構是怎么樣的?

幾乎所有人在被面試或者面試別人的的時候,都會(被)問到這個問題,很多人會回答,我們架構是MVC(MVVM),少數人還會使用MVP或者VIPER,我們姑且都稱為MV(X),但是真的架構僅僅就是MV(X)嗎?其實我覺得MV(X)雖然是架構中比較重要的部分,但是還是遠遠不能說架構 = MV(X)。

為什么呢?帶着這個問題,我們來看第二個例子,在這個案例中,我們關注下面幾點:

  1. 架構是如何隨着業務的變化而變化的(這個也是對上面觀點的一個證實)。
  2. 我們談到架構就提的 MV(X),處於架構中的哪個部分。
  3. 通過”餓了么“的架構演變,體會一下每個階段的側重點是什么,對於架構有一個體系上的認識。

文章地址:

餓了么移動APP的架構演進
https://www.jianshu.com/p/2141fb0dc62c

”餓了么“的架構經歷了4個階段的演化:

  1. 第一階段 MVC
  2. 第二階段 Module Decoupled (組件化)
  3. 第三階段 Hybrid
  4. 第四階段 React-Native & Hot Patch

第一階段 MVC

這個古老而經典的模式,不用多說。它是一個軟件”從無到有“,”短平快“開發的首選。也是大部分規模比較小的 App 幾乎大部分時間精力都會與之打交道的一個架構,以至於人們提架構比彈MVC。

當然這個架構隨着業務的劇增,很快就會出現弊端,朝着Massive-View-Controller的方向奔去。

第二階段 Module Decoupled

隨着代碼量不斷增加,功能模塊越來越多,不管是分工開發協作,還是已有模塊的復用和維護,組件化都成了這個階段的重點。組件化有個兩個關鍵:

  1. 如何划分組件。
  2. 如何實現組件之間的通信。

對於第一個問題,”餓了么“采用的方案,基本是業界廣為使用的分類方案,將組件分為共有組件和業務組件,

  • 公共組件提供了一些業務無關的基礎服務:比如網絡庫、數據庫、JSONModel等
  • 業務組件則對應具體的一塊業務,比如登錄業務組件,訂單組件等

對於兩種組件的管理:

  • 對於公共組件,使用CocoaPods進行版本管理(這點和我們目前不太一樣,因為我們是SDK提供方,我們引用的第三方庫,不確定我們的SDK使用方是否使用,是否更改源碼,所以我們的方式,是將穩定版本的源碼,混淆后打包進我們的代碼)。
  • 對於業務組件,這個和我們大體類似,采取了業務模塊注冊機制的方式來達到解耦的目的,每個業務模塊對外提供相應的業務接口,再啟動時向一個中心注冊自己的Scheme(我們是協議)。

而在具體某個業務組件內部,則可以根據不同開發人員,不同隊伍的偏好,來實現不同的代碼架構,比如MVC、MVVM、MVP等,也都不會影響整體系統架構。

這時的架構圖,看上去長這樣:

我們可以看到,MV(X) 已經不是關注的全部了,很多模塊已經和 MV(X) 不怎么搭邊了。

所以說,架構不等於 MV(X),其實 MV(X) 關注的只是”應用層“的部分

關於分層:
一般的,可以將App分為三層:應用層、service層、data access層。

  • 應用層 是直接和用戶打交道的部分,我們常用到的 UIViewController,Android的 Activity,負責了數據的展示、流向、用戶交互的處理。
  • service 層 是在應用層的下面,為應用層服務器的,對於應用層來說就像一個API調用延遲為0ms的Server API。一般會放在應用層的代碼:網絡接口調用、公共系統服務API(GPS定位、隱私權限訪問)、一些 UTil 代碼(所以我覺得比如一個 UIViewController 的一些私有方法和一些提工具性質的category,其實應該算serveice 層)。
  • data access 提供和對於數據的”增刪改查“的接口層。

第三階段 Hybrid

業務的變化又來啦,當用戶規模達到比較大的數量,這次不僅僅是功能的增加,每兩周一版已經滿足不了產品、運營躁動的心了;同時,用純 Native 代碼編寫的 App,如果上線后有錯誤,只能等下一次提交市場。在如今互聯網競爭如此激烈的時代,一次線上錯誤有時也會帶來很大的影響。所以這時候,很多純粹展示性的模塊會使用 H5 的方式來實現。

但是這種方式也有它的弊端:

  • 每次加載頁面需要請求服務器,渲染時間比較長。
  • 調用本地硬件設備存在一定的不便。

對於這個問題,也有很多方案可以權衡,比如可以提前將網頁打包好,以減少網絡傳輸的時間,同時提供一系列的插件來訪問本地的硬件設備。

“餓了么”這里的做法是,綜合了 Native 和 H5 的優缺點,將頁面做了一個划分,純粹展示性的模塊使用 H5;而更多的數據操作、動畫渲染性的模塊使用 Native。

架構圖長成這樣子:

業務再一次再架構的演化中扮演了重要的角色。

第四階段 React-Native & Hot Patch

又要頻繁迭代,又要用戶體驗,這時就考慮到了RN;另外,餓了么這個階段用戶已經過億,線上一個小 bug 都可能影響幾萬人的使用,所以這個階段,重點在於 RN 模塊的引入,以及 Hot Patch 熱修復功能的引入。

在 RN 的使用方面,依然有一個取舍,要回答下面的問題:

  • 哪些頁面使用 RN,哪些頁面不用 RN。
  • 是整個模塊使用 RN,還是一個模塊的部分頁面使用 RN。
  • RN 和 Native 頁面是2選1的關系,還是說是一個備份。
  • RN 和 Native 頁面如何通信。

“餓了么”的做法是:對於20%最重要的頁面,做了一個 RN 的鏡像,也就是一個備份,然后通過服務器的配置,來切換Native 還是 RN,這樣如果 Native 頁面出現問題的時候,先通過開關將線上的頁面切換成 RN,先保證線上正常使用,然后使用 Hot Patch 完成修補后,再切換回 Native App 原生頁面。

這時的架構圖:

不得不說,這種做法不一定適合別的團隊,畢竟一個頁面,要寫 Native 、 RN 兩套代碼,並且要一直維護,花的代價都有點大,不是每個團隊都有精力去這么搞的。其實這點,也正說明了,你需要根據自己業務,設計出一個最適合自己項目的架構。

小結

小結一下:

  1. 業務一直在影響架構的變遷。
  2. MV(X) 其實只是“應用層”的事,對於架構應該有個系統的認識。
  3. 架構的設計,並不是有現成的拿來用就 OK 的事,還有很多細節的部分需要做取舍,依賴業務需求和經驗。

第三個案例:《猿題庫 iOS 客戶端架構設計》—— 好的架構具有哪些特質

第三個案例我們回歸 MV(X),畢竟它確實是我們日常開發接觸比較多的一部分。

對了這個案例,想關注的“點”是

  1. MVC 和 MVVM 的優缺點。
  2. 如何能夠規避缺點,結合優點,改進架構,設計一個適合自己的MV(X)架構。
  3. 這個思想的底層原理是什么,在別的場景下的設計能夠通用。

文章地址:

猿題庫 iOS 客戶端架構設計
http://gracelancy.com/blog/2016/01/06/ape-ios-arch-design/

MVC

優點:

  1. 易理解,對應現實生活中也是這樣的。
  2. 易上手,iOS、Android 默認就是個 MVC 的環境。

缺點:

  1. 當指責不是那么明確,不知道該放哪時,代碼就會被放在"Controller"里面吧,Controller越來越難維護。

其實對於上面這個缺點,唐巧也在一篇文章中寫道,這個問題其實也不能說是 MVC 的缺點,是我們沒有拆分好代碼。可以看看唐巧的《被誤解的 MVC 和被神化的 MVVM》,提出了一些如何解決 Controler 臃腫的解決辦法,然后也表達了對於 MVVM 的質疑,具體的做法可以去讀這篇文章。這也正說明了大家對於架構的理解和態度真的是有區別的。

MVVM

具體關於MVVM的概念可以參考 Objc 的《MVVM 介紹》,這里就不具體說 MVVM 的概念了。

不了解MVVM的同學,知道這幾點就行:

  1. MVVM將ViewController視作View。
  2. 關於 View Model,只需要知道兩件事:持有model;View可以完全通過一個View Model決定自己如何展示。
  3. View 和 View Model,View Model 和 Model之間通過數據綁定,使得 Model改變的時候,能同步更新 View Model,進而更新 View。

優點:

  1. 減輕了 Controller 的負擔,拆分了代碼
  2. View Model有比較好的測試性。
  3. 結合 RAC, 可以將數據和 View 通信的代碼精簡到很少。

缺點:

  1. 上手成本高。
  2. 由於使用數據綁定,界面的 bug 變的不易調試。
  3. ViewModel 接管了 ViewController 的大部分職責,慢慢也可能變的臃腫。

綜合兩者

來看下 Lancy 的設計,是如何將兩者綜合,規避缺點,保留優點的,先上圖:

對於上圖的說明:

  • 一個View Controller 持有一個 Data Controller。
  • Data Controller,是數據管理模塊,負責數據的生命周期:獲取、保存、更新。
  • 一個 View Controller 里面有多個 View,每個 View 對應一個 View Model,這里的 View Model 概念和 MVVM 里的類似,唯一不同的是這里的 View Model 和 Model ,沒有綁定機制。
  • View 的展示樣式,完全決定於 View Model。

結合產品 UI,再按照數據流的方式闡述,以下面的 CollectionView 為例。

  1. View Controller 持有 一個 Data Controller,初始化之后,調用 Data Controller 獲取用戶打開的課程。(1)
  2. Data Controller 通過 API 獲取數據,封裝成 Model 並返回 (2,3)
  3. View Controller 將2中返回的數據,生成 View Model,調用 View 的 bindDataWithViewModel 方法裝配給對應的 View。(4)
  4. View Controller 會調用 View 的渲染方法,View 通過 View model 直接進行渲染。(5)
  5. 如果有用戶事件,通過代理的方式,傳遞給 View Controller,讓View Controller 來決定下一步的處理。(6)

這么方式的優缺點:

優點:

  1. 指責分明,確定給 Controller 肩負。
  2. 耦合度低,測試性高。指責分明帶來的效果就是耦合度低,同一個功能,可以分別由不同的開發人員分別進行開發界面和邏輯,只需要確立好接口即可。
  3. 學習成本低,不用事件綁定,不需要學習 RAC。
  4. 易於調試 Bug,不使用綁定帶來的好處。

缺點:

  1. 當頁面的交互邏輯非常多時,需要頻繁的在 DC-VC-VM 里來回傳遞信息,造成了大量膠水代碼。
  2. 沒有綁定,帶來額外的代碼(綁定真的是雙刃劍)

小結

針對這個案例,我覺得最應該我們思考的就是,作者 Lancy,在設計架構的時候的思路是怎么樣的?為什么要那么設計?是怎么取舍的?總結一下:

  1. 因為想讓團隊能夠快速上手,以及bug可以快速調試,所以沒有使用綁定機制。(從學習成本、開發成本、以調試角度)
  2. 依然保證了指責的明確划分。(好的架構一定要明確划分職責,甚至均衡的划分)
  3. 方便測試依然是重要的一個設計標准。(好的架構要易於測試)
  4. 還有相當重要的一個標准——解耦(好的架構要易於維護,解耦意味着比較易於維護)
  5. 上面提到了缺點之一是“當頁面交互邏輯非常多時,會不太合適”,這也說明了,作者采用了這個架構,其實是基於頁面交互不是很多的情況(用戶交互確實帶來Model的改動不是很多,當前界面並不能修改用戶所開的課程)。所以業務依然是影響架構設計的總要因素。
  6. 還有一點不知道大家有沒有在意,上面提到了“數據流”,對着這個架構我們能清晰的說出“數據流”,這個我認為也是一個好的架構應該具有的特性。數據流如果很模糊,有很多分支,那我們的維護成本將大大增加,一個清晰的數據流,意味着你無論在這個流的那個節點繼續執行下需,都能得到正確的結果。

基於對這個案例的分析,最應該思考的是,設計一個架構的思路,換言之,你要心里明白,怎樣才是一個好的架構。

總結

總結一下,今天說的這三個案例,其實就是為了說明一下幾點:

  1. 懂業務對於架構的重要性。
  2. 架構 != MV(X),站在更加宏觀的角度看問題,對於打開思路更有幫助。
  3. 當我們設計架構的時候,怎樣才是一個好的架構。

其實“架構”真的是個很大的話題,很多知識都可以拿出來單獨學習和分享。

  • 設計原則和設計模式(設計的基本功)
  • 數據結構和算法(設計的基本功)
  • MV(X)/Viper
  • 組件化 (光這個就特別多可以講的)
  • 網絡層的架構設計(比如離散的還是集約的)
  • 持久化層的設計
  • Hybrid 的設計
  • RN、Hot Patch
  • 無埋點
  • pod 私有庫維護 SDK
  • 面向過程/面向對象
  • AOP
  • 。。。。。

希望以后能陸續的為大家分享,擅長哪個方向,或者對哪個方向感興趣的小伙伴也可以給大家分享一下,讓大家的設計能力一點點提高上來。

參考

MVVM 介紹
https://objccn.io/issue-13-1/
被誤解的 MVC 和被神化的 MVVM
http://blog.devtang.com/2015/11/02/mvc-and-mvvm/
iOS應用架構談系列
https://casatwy.com/iosying-yong-jia-gou-tan-kai-pian.html
猿題庫 iOS 客戶端架構設計
http://gracelancy.com/blog/2016/01/06/ape-ios-arch-design/
餓了么移動APP的架構演進
https://www.jianshu.com/p/2141fb0dc62c
iOS應用層架構之CDD
http://mrpeak.cn/blog/cdd/
iOS應用架構現狀分析
http://mrpeak.cn/blog/ios-arch/
iOS 架構模式–解密 MVC,MVP,MVVM以及VIPER架構
http://www.cocoachina.com/ios/20160108/14916.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM