爲什麼需要組件化
隨着公司業務的不斷髮展,項目的功能越來越複雜,各個業務代碼耦合也越來越多,代碼量也是急劇增加,傳統的MVC或者MVVM架構已經無法高效的管理工程代碼,因此需要用一種技術來更好地管理工程,而組件化是一種能夠解決代碼耦合的技術。項目經過組件化的拆分,不僅可以解決代碼耦合的問題,還可以增強代碼的複用性,工程的易管理性等等。
iOS業界討論組件化方案甚多,大體來說有3種。
-
Protocol註冊方案
-
URL註冊方案
-
Target-Action runtime調用方案
URL註冊方案據我瞭解很多大公司都在採用,蘑菇街 App 的組件化之路蘑菇街的Limboy在這篇博客中做了很詳盡的闡述
Target-Action runtime調用方案Casa在 iOS應用架構談 組件化方案中也做了很詳盡的描述,前陣時間Casa開了一篇博客在現有工程中實施基於CTMediator的組件化方案清楚講述瞭如何用這套方案實施組件化
**Protocol方案 阿里BeeHive框架入門 https://www.jianshu.com/p/35aeb63acfb2
image.png
image.png
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,每個模塊都離不開其他模塊,互相依賴粘在一起成爲一坨:
[圖片上傳失敗...(image-650e5b-1522291343934)]
耦合比較嚴重(因爲沒有明確的約束,「組件」間引用的現象會比較多)
- 問題2,多人同時開發時,容易出現衝突(尤其是Xcode Project文件)
- 問題3,業務方的開發效率不夠高(只關心自己的組件,卻要編譯整個項目,與其他不相干的代碼糅合在一起)
2、組件化的好處?
一般意義:
- 加快編譯速度(不用編譯主客那一大坨代碼了);
- 各組件自由選擇開發姿勢(MVC / MVVM / FRP);
- 組件工程本身可以獨立開發測試,方便 QA 有針對性地測試;
- 規範組件之間的通信接,讓各個組件對外都提供一個黑盒服務,減少溝通和維護成本,提高效率;
對於公司已有項目的現實意義:
- 業務分層、解耦,使代碼變得可維護;
- 有效的拆分、組織日益龐大的工程代碼,使工程目錄變得可維護;
- 便於各業務功能拆分、抽離,實現真正的功能複用;
- 業務隔離,跨團隊開發代碼控制和版本風險控制的實現;
- 模塊化對代碼的封裝性、合理性都有一定的要求,提升開發同學的設計能力;
- 在維護好各級組件的情況下,隨意組合滿足不同客戶需求;(只需要將之前的多個業務組件模塊在新的主App中進行組裝即可快速迭代出下一個全新App)
3、什麼情況下進行組件化比較合適?
當然組件化也有它的缺點:
-
學習成本高,對於開發人員對各種工具的掌握要求也比較高,對於新手來說入門較爲困難。
-
由於工具和流程的複雜化,導致團隊之間協作的成本變高,某些情況下可能會導致開發效率下降。
當項目App處於起步階段、各個需求模塊趨於成熟穩定的過程中,組件化也許並沒有那麼迫切,甚至考慮組件化的架構可能會影響開發效率和需求迭代。
而當項目迭代到一定時期之後,便會出現一些相對獨立的業務功能模塊,而團隊的規模也會隨着項目迭代逐漸增長,這便是中小型應用考慮組件化的時機了。這時爲了更好的分工協作,團隊安排團隊成員各自維護一個相對獨立的業務組件是比較常見的做法。
在這時這個時候來引入組件化方案,是比較合適的時機。長遠來看,組件化帶來的好處是遠遠大於壞處的,特別是隨着項目的規模增大,這種好處會變得越來越明顯
三、如何組件化?
組件化的開展需要解決以下幾個層次的問題:
1、組件化的架構目標?
借用Limboy的圖:
[圖片上傳失敗...(image-8de174-1522291343934)]
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:
image.png
這樣一來,各個模塊直接都不需要再互相依賴,而是僅需要依賴 Mediator 層即可。
可直觀上看,這樣做並沒有什麼好處,依賴關係並沒有解除,Mediator 依賴了所有模塊,而調用者又依賴 Mediator,最後還是一坨互相依賴,跟原來沒有 Mediator 的方案相比除了更麻煩點其他沒區別。
我們希望最終能過實現的是單向的依賴,即:
image.png
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系列教程(三)——私有庫管理和模塊化管理