摘自原文:https://www.cnblogs.com/fishbay/p/7216084.html
最近研究了一下項目的組件化,把casa、bang、limboy的有關組件化的博客看了一遍,學到了不少東西,對目前業(yè)界的組件化方案有了一定的了解。這些高質量的博客大致討論了組件化的三種方案:url-block、protocol-class(和url-controller類似)、target-action,以及應用這三種組件化方案的時機、步驟、利弊等等。
本文主要介紹一下這三種組件化方案的技術實現(xiàn)過程,針對不同組件化方案具體應用過程中可能出現(xiàn)的問題加以介紹,也針對casa批判蘑菇街的組件化方案加以自己的思考,希望對需要了解組件化的朋友有一定的幫助。
為什么需要組件化
隨著公司業(yè)務的不斷發(fā)展,項目的功能越來越復雜,各個業(yè)務代碼耦合也越來越多,代碼量也是急劇增加,傳統(tǒng)的MVC或者MVVM架構已經無法高效的管理工程代碼,因此需要用一種技術來更好地管理工程,而組件化是一種能夠解決代碼耦合的技術。項目經過組件化的拆分,不僅可以解決代碼耦合的問題,還可以增強代碼的復用性,工程的易管理性等等。
組件化的過程
之前根據(jù)蘑菇街的組件化方案,limboy和casa等人做了深入的討論,并根據(jù)各自的觀點給出了方案實施的理由以及利弊關系,然后又有人改進了他們的組件化方案,我總結了一下,大致有三種,下面分別介紹各自的實現(xiàn)過程:
方案一、url-block
這是蘑菇街中應用的一種頁面間調用的方式,通過在啟動時注冊組件提供的服務,把調用組件使用的url和組件提供的服務block對應起來,保存到內存中。在使用組件的服務時,通過url找到對應的block,然后獲取服務。
下圖是url-block的架構圖:
注冊:
[MGJRouter registerURLPattern:@"mgj://detail?id=:id"toHandler:^(NSDictionary*routerParameters) {? ? NSNumber *id = routerParameters[@"id"];// create view controller with id? ? // push view controller}];
調用:
[MGJRouter openURL:@"mgj://detail?id=404"]
蘑菇街為了統(tǒng)一iOS和Android的平臺差異性,專門用后臺來管理url,然后針對不同的平臺,生成不同類型的文件,來方便使用。
使用url-block的方案的確可以組建間的解耦,但是還是存在其它明顯的問題,比如:
需要在內存中維護url-block的表,組件多了可能會有內存問題
url的參數(shù)傳遞受到限制,只能傳遞常規(guī)的字符串參數(shù),無法傳遞非常規(guī)參數(shù),如UIImage、NSData等類型
沒有區(qū)分本地調用和遠程調用的情況,尤其是遠程調用,會因為url參數(shù)受限,導致一些功能受限
組件本身依賴了中間件,且分散注冊使的耦合較多
方案二、protocol-class
針對方案一的問題,蘑菇街又提出了另一種組件化的方案,就是通過protocol定義服務接口,組件通過實現(xiàn)該接口來提供接口定義的服務,具體實現(xiàn)就是把protocol和class做一個映射,同時在內存中保存一張映射表,使用的時候,就通過protocol找到對應的class來獲取需要的服務。
下圖是protocol-class的架構圖:
注冊:
[ModuleManager registerClass:ClassA forProtocol:ProtocolA]
調用:
[ModuleManager classForProtocol:ProtocolA]
蘑菇街的這種方案確實解決了方案一中無法傳遞非常規(guī)參數(shù)的問題,使得組件間的調用更為方便,但是它依然沒有解決組件依賴中間件的問題、內存中維護映射表的問題、組件的分散調用的問題。設計思想和方案一類似,都是通過給組件加了一層wrapper,然后給使用者調用。
同時,另一種方案是url-controller,這是LDBusMediator的組件化方案,我認為和方案二的實現(xiàn)原理類似。它是通過組件實現(xiàn)公共協(xié)議的服務,來對外提供服務。具體就是通過單例來維護url-controller的映射關系表,根據(jù)調用者的url,以及提供的參數(shù)(字典類型,所以參數(shù)類型不受約束)來返回對應的controller來提供服務;同時,為了增強組件提供服務的多樣性,又通過服務協(xié)議定義了其它的服務。整體來看,LDBusMediator解決了蘑菇街的這兩種組件化方案的不足,比如:通過注冊封裝件connector而不是block來降低了內存占用;通過字典傳遞參數(shù),解決了url參數(shù)的限制性。但是,由于使用了connector來提供服務而不是組件本身,把connector作為組件的一部分,依然有組件依賴中間件的問題。
下圖是LDBusMediator的組件化架構圖:
方案三、target-action
casa的方案是通過給組件包裝一層wrapper來給外界提供服務,然后調用者通過依賴中間件來使用服務;其中,中間件是通過runtime來調用組件的服務,是真正意義上的解耦,也是該方案最核心的地方。具體實施過程是給組件封裝一層target對象來對外提供服務,不會對原來組件造成入侵;然后,通過實現(xiàn)中間件的category來提供服務給調用者,這樣使用者只需要依賴中間件,而組件則不需要依賴中間件。
下圖是casa的組件化方案架構圖:
以下代碼來自casa的組件化demo
target
A組件
// TargetA.h- (UIViewController*)Action_nativeFetchDetailViewController:(NSDictionary*)params;
CTMediator分類
// CTMediator+CTMediatorModuleAActions.h- (UIViewController*)CTMediator_viewControllerForDetail;// CTMediator+CTMediatorModuleAActions.m- (UIViewController*)CTMediator_viewControllerForDetail{return[selfperformTarget:kCTMediatorTargetA action:kCTMediatorActionNativFetchDetailViewController params:@{@"key":@"value"} shouldCacheTarget:NO];}
調用
// ViewController.h#import"CTMediator+CTMediatorModuleAActions.h"[selfpresentViewController:[[CTMediator sharedInstance] CTMediator_viewControllerForDetail] animated:YEScompletion:nil];
從以上代碼可以看出,使用者只需要依賴中間件,而中間件又不依賴組件,這是真正意義上的解耦。但是casa的這個方案有個問題就是hardcode,在中間件的category里有hardcode,casa的解釋是在組件間調用時,最好是去model化,所以不可避免的引入了hardcode,并且所有的hardcode只存在于分類中。針對這個問題,有人提議,把所有的model做成組件化下沉,然后讓所有的組件都可以自由的訪問model,不過在我看來,這種方案雖然解決了組件間傳遞model的依賴問題,但是為了解決這個小問題,直接把整個model層組件化后暴露給所有組件,容易造成數(shù)據(jù)泄露,付出的代價有點大。針對這個問題,經過和網友討論,一致覺得組件間調用時用字典傳遞數(shù)據(jù),組件內調用時用model傳遞數(shù)據(jù),這樣即減少組件間數(shù)據(jù)對model的耦合,又方便了組件內使用model傳遞數(shù)據(jù)的便捷性。
組件化實施的方式
組件化可以利用git的源代碼管理工具的便利性來實施,具體就是建立一個項目工程的私有化倉庫,然后把各個組件的podspec上傳到私有倉庫,在需要用到組件時,直接從倉庫里面取。
1.封裝公共庫和基礎UI庫
在具體的項目開發(fā)過程中,我們常會用到三方庫和自己封裝的UI庫,我們可以把這些庫封裝成組件,然后在項目里用pod進行管理。其中,針對三方庫,最好再封裝一層,使我們的項目部直接依賴三方庫,方便后續(xù)開發(fā)過程中的更換。
2.獨立業(yè)務模塊化
在開發(fā)過程中,對一些獨立的模塊,如:登錄模塊、賬戶模塊等等,也可以封裝成組件,因為這些組件是項目強依賴的,調用的頻次比較多。另外,在拆分組件化的過程中,拆分的粒度要合適,盡量做到組件的獨立性。同時,組件化是一個漸進的過程,不可能把一個完整的工程一下子全部組件化,要分步進行,通過不停的迭代,來最終實現(xiàn)項目的組件化。
3.服務接口最小化
在前兩步都完成的情況下,我們可以根據(jù)組件被調用的需求來抽象出組件對外的最小化接口。這時,就可以選擇具體應用哪種組件化方案來實施組件化了。
總結
組件化是項目架構層面的技術,不是所有項目都適合組件化,組件化一般針對的是大中型的項目,并且是多人開發(fā)。如果,項目比較小,開發(fā)人員比較少,確實不太適合組件化,因為這時的組件化可能帶來的不是便捷,而是增加了開發(fā)的工作量。另外,組件化過程也要考慮團隊的情況,總之,根據(jù)目前項目的情況作出最合適的技術選型。