隨着應用需求逐步迭代,應用的代碼體積將會愈來愈大,爲了更好的管理應用工程,咱們開始藉助CocoaPods版本管理工具對原有應用工程進行拆分。可是僅僅完成代碼拆分還不足以解決業務之間的代碼耦合,爲了更好的讓拆分出去的業務工程可以獨立運行,必須進行組件拆分而且實現組件服務化。css
下面是最近在行業內幾個大神的博客辯論對戰,具體資料以下:html
最近在參考大神們的討論和以前的LDBusBundle方案基礎上上,提煉出了一個適合中小型應用的LDBusMediator中間件,正逐漸在項目中使用。java
博客介紹:http://www.jianshu.com/p/196f66d31543
中間件Git開源地址:https://github.com/Lede-Inc/LDBusMediator.gitandroid
2016.03.10 蘑菇街App的組件化之路: http://limboy.me/ios/2016/03/10/mgj-components.htmlios
[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」]
短鏈如何管理?git
方法一:經過url的方式github
[MGJRouter registerURLPattern:@"mgj://cart/ordercount" toObjectHandler:^id(NSDictionary *routerParamters){ // do some calculation return @42; }] NSNumber *orderCount = [MGJRouter objectForURL:@"mgj://cart/ordercount」]
方法二:經過protocol-class對應的方式sql
把公共協議文件統一放到PublicProtocolDomain.h中,全部業務組件只依賴這個文件;protocol只能經過類方法提供?安全
@protocol MGJCart <NSObject> + (NSInteger)orderCount; @end [ModuleManager registerClass:MGJCartImpl forProtocol:@protocol(MGJCart)] [ModuleManager classForProtocol:@protocol(MGJCart)]
啓動初始化時,實例APP中全部組件的module實例,讓每一個組件的module實例執行一遍didFinishLaunchingWithOptions方法:在這方法中每一個組件註冊本身的URL,使用class註冊;每一個組件能夠自行監控系統的通知,如UIApplicationDidBecomeActiveNotification, 對於沒有系統通知消息則將此方法寫入module的protocol中,依次執行實例的這些protocol方法;ruby
[[ModuleManager sharedInstance] loadModuleFromPlist:[[NSBundle mainBundle] pathForResource:@"modules" ofType:@"plist"]]; NSArray *modules = [[ModuleManager sharedInstance] allModules]; for (id<ModuleProtocol> module in modules) { if ([module respondsToSelector:_cmd]) { [module application:application didFinishLaunchingWithOptions:launchOptions]; } }
MGJRouter: https://github.com/mogujie/MGJRouter.git
基於Mediator模式和Target-Action模式:
[CTMediator sharedInstance]
openUrl:url] //call from other app with url parseUrl performTarget:action:params //call form Native Module runtime [TargetA action1], [TargetA action2] [TargetB action1], [TargetB action2]
本地跨組件間調用:
[[CTMediator sharedInstance] performTarget:targetName action:actionName params:@{…}]
遠程應用調用:
openUrl + parseUrl的方式; 針對請求的路由操做,直接將Target和Action的名字封裝到url中;
代碼Git地址:https://github.com/casatwy/CTMediator.git
基礎功能組件:(相似於性能統計、Networking、Patch、網絡診斷等)
基礎UI組件:(例以下拉刷新組件、iCausel相似的組件)
產品業務組件:(例如圈子、1元購、登陸、客服MM等)
組件化頁面跳轉(UIBus)方案要求:
組件服務化(ServiceBus)方案要求:
(通用問題:複雜參數傳遞 key值的硬編碼問題)
組件服務接口的設計:
版本發佈:
MGJRouter+ModuleManager方案 (蘑菇街方案)
CTMediator+Target-Action方案 (反革命方案)
組件化主要仍是解決本地業務組件間的調用,至於跨App或者Hybrid頁面經過openUrl方式調用頁面和服務的方式實際上是能夠拆分紅兩個步驟的問題:特定模塊解析處理+中間件調用。跨App經過info.plist配置的scheme跳轉進入,hybrid頁面經過JSBridge框架跳轉進入,這部分都有特定的模塊去解析完成。在特定的模塊中是否要調用其它業務組件的頁面或者服務由特定模塊自行決定,這不是組件化中間件要去完成的事情。
從實際開發來講,組件之間最大的需求就是頁面跳轉,須要從組件A的pageA1頁面跳轉到組件B的pageB1頁面,避免對組件B頁面ViewController頭文件的直接依賴。其次就是服務的調用,服務調用模塊毫不是爲了解決url跳轉的問題,只是服務調用方式能夠用來解決頁面跳轉的需求,可是沒有url跳轉方案成本低。因此纔有了蘑菇街方案的MGJRouter和ModuleManager的class-protocol方案的區別;而反革命的方案仍然用Target-Action方案來解決頁面跳轉問題,成本稍大;並且url跳轉和服務調用是兩種不一樣的組件間通訊需求,用兩種不一樣的方式來完成更有區分度。
中間件若是涉及到具體的業務邏輯,勢必形成中間件對業務模塊的直接依賴,因此中間件只須要抽象出業務通訊的基本職責,規定好協議接口,完成調度功能便可。
而每一個掛接節點(這裏指業務組件)遵循中間件的協議完成掛接工做,固然這會形成掛接節點對中間件的協議依賴;調用方一樣也必須經過掛接點提供的方法將調用操做push到中間件上,而不用管具體的調用過程,這樣也是掛接節點依賴中間件,業務邏輯並無直接依賴中間件。這就是以前阿里無線分享的bus總線的思路,經過這種思路即便切換或者去掉中間件,都只須要在掛接節點中進行修改就能夠完成,避免了對業務邏輯代碼的直接調用修改。
至於去掉中間件,應用仍然可以跑的命題? 若是沒有任何代碼的修改,就至關於把解藕的橋樑給拆除了,再牛逼的框架也不能知足。
中間件解決了組件間的通訊解藕問題,勢必會將組件對外提供調用的信息隱藏起來,否則就不能達到解藕通訊的目標。
蘑菇街方案的披露方法:
(是否把url短鏈和publicProtocol文件統一放到一個repo裏,其實就至關於說明文檔的做用)
反革命方案的披露方法:
以前聽阿里的組件化分享以後,本身作了一套有關Bus總線的方案,可是在具體的產品使用過程當中用起來仍是麻煩,在項目中推廣起來難度仍是比較大。特別是關於組件對外披露信息的部分,到如今都沒有一個好的思路,雖然反革命的方案解決了披露的問題,可是我以爲擴展性和可維護性上仍是比較差。
git開源地址:https://github.com/Lede-Inc/LDBusBundle_IOS.git
最近研讀幾個大神的博客和討論以後,有了一些新的思路,但願可以繼續按照bus+category的思路上去專研一下,但願可以一個真正適合在項目裏推行起來的方案。
最近在參考大神們的討論和以前的LDBusBundle方案基礎上上,提煉出了一個適合中小型應用的LDBusMediator中間件,正逐漸在項目中使用。
博客介紹:http://www.jianshu.com/p/196f66d31543
中間件Git開源地址:https://github.com/Lede-Inc/LDBusMediator.git