iOS組件化方案探索
一、什么是組件化?
1、什么是組件?
"組件"
一般來說用于命名比較小的功能塊,如:下拉刷新組件、提示框組件。而較大粒度的業務功能,我們習慣稱之為"模塊"
,如:首頁模塊、我的模塊、新聞模塊。
這次討論的主題是組件化,這里為了方便表述,下面模塊和組件代表同一個意思,都是指較大粒度的業務模塊。
2、什么是組件化?
組件化,或者說模塊化,用來分割、組織和打包軟件。每個模塊完成一個特定的子功能,所有的模塊按某種方法組裝起來,成為一個整體,完成整個系統所要求的功能。
從工程代碼層面來說,組件化的實施通常是通過中間件
解決組件間頭文件直接引用、依賴混亂的問題;從實際開發來說,組件之間最大的需求就是頁面跳轉,需要從組件A的pageA頁面跳轉到組件B的pageB頁面,避免對組件B頁面ViewController頭文件的直接依賴。
二、為什么要組件化?
從兩個方面論述:
1、組件化是為了解決什么問題?
一個 APP 有多個模塊,模塊之間會通信,互相調用,如我們的證券app,有首頁、行情、資訊、我的等模塊。這些模塊會互相調用,例如 首頁底部需要展示部分資訊、行情;行情底部需要展示個股資訊;資訊詳情頁需要跳轉到行情,等等。
一般我們是怎樣調用呢,以首頁調用資訊為例,會這樣寫:
#import "HomeViewController.h"
#import "NewsViewController.h"
@implementation HomeViewController
+ (void)gotoNews {
NewsViewController *detailVC = [[NewsViewController alloc] initWithStockCode:self.codeNum];
[self.navigationController.pushViewController:detailVC animated:YES];
}
@end
看起來挺好,這樣做簡單明了,沒有多余的東西,項目初期推薦這樣快速開發,但到了項目越來越龐大,這種方式會有什么問題呢?
- 問題1,每個模塊都離不開其他模塊,互相依賴粘在一起成為一坨:
耦合比較嚴重(因為沒有明確的約束,「組件」間引用的現象會比較多)
- 問題2,多人同時開發時,容易出現沖突(尤其是Xcode Project文件)
- 問題3,業務方的開發效率不夠高(只關心自己的組件,卻要編譯整個項目,與其他不相干的代碼糅合在一起)
2、組件化的好處?
一般意義:
- 加快編譯速度(不用編譯主客那一大坨代碼了);
- 各組件自由選擇開發姿勢(MVC / MVVM / FRP);
- 組件工程本身可以獨立開發測試,方便 QA 有針對性地測試;
- 規范組件之間的通信接,讓各個組件對外都提供一個黑盒服務,減少溝通和維護成本,提高效率;
對于公司已有項目的現實意義:
- 業務分層、解耦,使代碼變得可維護;
- 有效的拆分、組織日益龐大的工程代碼,使工程目錄變得可維護;
- 便于各業務功能拆分、抽離,實現真正的功能復用;
- 業務隔離,跨團隊開發代碼控制和版本風險控制的實現;
- 模塊化對代碼的封裝性、合理性都有一定的要求,提升開發同學的設計能力;
- 在維護好各級組件的情況下,隨意組合滿足不同客戶需求;(只需要將之前的多個業務組件模塊在新的主App中進行組裝即可快速迭代出下一個全新App)
3、什么情況下進行組件化比較合適?
當然組件化也有它的缺點:
學習成本高,對于開發人員對各種工具的掌握要求也比較高,對于新手來說入門較為困難。
由于工具和流程的復雜化,導致團隊之間協作的成本變高,某些情況下可能會導致開發效率下降。
當項目App處于起步階段、各個需求模塊趨于成熟穩定的過程中,組件化也許并沒有那么迫切,甚至考慮組件化的架構可能會影響開發效率和需求迭代。
而當項目迭代到一定時期之后,便會出現一些相對獨立的業務功能模塊,而團隊的規模也會隨著項目迭代逐漸增長,這便是中小型應用考慮組件化的時機了。這時為了更好的分工協作,團隊安排團隊成員各自維護一個相對獨立的業務組件是比較常見的做法。
在這時這個時候來引入組件化方案,是比較合適的時機。長遠來看,組件化帶來的好處是遠遠大于壞處的,特別是隨著項目的規模增大,這種好處會變得越來越明顯
三、如何組件化?
組件化的開展需要解決以下幾個層次的問題:
1、組件化的架構目標?
借用Limboy的圖:
2、如何劃分組件?
- 基礎功能組件
- 基礎產品組件
- 個性化業務組件
對于一個沒有實施過組件化拆分的工程來說,其中很可能充滿了大量不合理的類、方法、頭文件和各種錯亂的依賴關系,因此首先要進行的第一步是模塊拆分。
模塊拆分可以分成兩個部分,基礎模塊拆分和業務模塊拆分。基礎模塊通常是穩定的依賴代碼,業務模塊是涉及到業務的需要頻繁改動的代碼。
基礎模塊拆分
基礎模塊是任何一個App都需要用到的,如:性能統計、Networking、Patch、網絡診斷、數據存儲模塊。對于基礎模塊來說,其本身應該是自洽的,即可以單獨編譯或者幾個模塊合在一起可以單獨編譯。所有的依賴關系都應該是業務模塊指向基礎模塊的。
基礎模塊之間盡量避免產生橫向依賴。
業務模塊拆分
對于業務模塊來說,考慮到舊有代碼可能沒有相關的橫向解耦策略,業務模塊之間的依賴會非常復雜,難以單獨進行拆分,因此我們采用的方法是首先從 group 角度進行重新整理。
對業務量很大的工程來說,我個人更加推薦“業務-分層”這樣的結構,而不是“分層-業務”,即類似下面的 group 結構:
- BusinessA
- Model
- View
- Controller
- Store
- BusinessB
- Model
- View
- Controller
-Store
而非目前項目中采用的:
- Controllers
- BusinessA_Controller
- BusinessB_Controller
- Views
- BusinessA_View
- BusinessB_View
- Models
- BusinessA_Model
- BusinessB_Model
3、組件化的技術難點?
組件化的實施,直觀上看,只是需要將各業務組件的代碼放到各自的文件夾或者 jar包里就行了。
這里引出的是:
3.1、組件的拆分方式問題:
可以利用CocoaPods 配合 git 做代碼版本管理,獨立業務模塊單獨成庫。
但這僅僅是物理上拆分了,拆分后的代碼編譯是肯定通不過的,因為如下:
#import "MainViewController.h"
#import "HomeViewController.h"
#import "NewsViewController.h"
#import "MeViewController.h"
#import ...
@implementation MainViewController
@end
MainViewController
會找不到依賴的其它各個模塊的頭文件而報錯。這里引出的又是另一個問題:
3.2、組件間如何解耦?
組件間解耦,是組件化必須解決的一個問題。換句話說,就是如何解除業務模塊間的橫向依賴。還是拿上邊舉得例子來說:
App的根視圖MainViewController
需要管理首頁、新聞、我的等等頁面時,如何做到 MainViewController
中,不用去 import
這一大堆 XXViewController
?
很簡單,按軟件工程的思路,下意識就會加一個中間層Mediator:
這樣一來,各個模塊直接都不需要再互相依賴,而是僅需要依賴 Mediator 層即可。
可直觀上看,這樣做并沒有什么好處,依賴關系并沒有解除,Mediator 依賴了所有模塊,而調用者又依賴 Mediator,最后還是一坨互相依賴,跟原來沒有 Mediator 的方案相比除了更麻煩點其他沒區別。
我們希望最終能過實現的是單向的依賴,即:
3.3、對此,可以參考業內的流行方案:
基于 URL Router、ModuleManager
代表:蘑菇街 Limboy基于 Target-Action、Runtime、Category
代表:安居客 casa
具體實現方案較為抽象,這里暫時先不詳細展開論述,可以參見Demo:
四、其它
開發流程控制
托管平臺選擇
自己利用開源的方案搭建私有的托管平臺,可以最大限制地保證代碼的安全。開源方案當中最知名也是最為廣泛使用的當屬 Gitlab。
組件化使我們從單一的主工程,變成了主工程+多個拆分好的基礎模塊+統一的私有 Spec 倉庫。為了避免某個人的工作對其他人開發環境造成影響,需要對整個組的開發流程進行統一的規范。
不管是對于主倉庫和子模塊倉庫,git-flow 都是首先推薦的工作流程。一個倉庫的 master 分支只有所有者可以有權限更改,其他的貢獻者想更改的話,需要自己創建新的分支(在 Github 上就是進行 fork),然后進行更改,之后把更改向原倉庫發送 Pull Request。Pull Request 就是一個合并的請求,其中可以看到貢獻者的更改,項目主人和其他維護者可以對 Pull Request 進行審核,共同探討修改意見。當項目主人認為修改 OK 之后,就可以合并這個 Pull Request ,把這部分代碼合并到主分支。
這個流程是完全分布式的,也就是說可以同時有多個貢獻者在不同的分支進行工作,最后統一合并到主分支上,實現并行協作。
同時在審核 Pull Request 階段,除了人工審核代碼之外,Github 還加入了對于持續集成的支持,可以檢測這個 Pull Request 是不是能夠通過測試的,進一步保證了代碼的質量。
組件維護問題?
待補充
五、參考資料:
相關技術博客:
4、《iOS應用架構談 組件化方案》和《蘑菇街 App 的組件化之路》的閱讀指導
11、模塊化與解耦
相關解決方案
私有Cocoapods實施方案
1、使用Cocoapods創建私有podspec - GeekerProbe
2、Cocoapods系列教程(三)——私有庫管理和模塊化管理