随着应用需求逐步迭代,应用的代码体积将会愈来愈大,为了更好的管理应用工程,咱们开始借助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