概述这是iOS组件化方案的第二部分-总结。在本文中,我实现了Target-Action方案的Demo,并与第一篇介绍的协议方案进行了对比。.如果你还没有看过我的第一篇协议组件化解决方案,可以先下载我文章中提供的Demo,从而了解我文章的细节,了解我的Demo中实现的业务场景,PortaliOSComponents第一篇Target-Action方案的章节,总结了国际惯例,先去demo(下载主工程,看不懂可以下载所有业务模块,Casa也提供了一个官方demo,我***本文提供传送门)原文可查看以下链接。Target-ActionScheme主项目Target-ActionScheme商品详情业务类别组件地址Target-ActionScheme商品详情业务模块地址Target-ActionScheme确认订单类别组件地址Target-ActionScheme确认订单业务模块地址Target-ActionSchemeCTMediator地址BecauseCasa并没有把CTMediator做成公共库,所以我直接copy了做成我的私有库。如何实现如何把modules打成privatepod我这里就不介绍了,想了解的可以看我的第一篇组件介绍文章。这里我只以确认订单模块为例。确认订单模块是一个单独的项目。为了防止其他模块调用确认订单模块,需要导入整个模块。这里又是一个确认订单业务Category的私有组件,如下图所示。TAConfirmOrderBusinessCategory是外部确认订单模块。提供服务的入口。我们的业务场景是商品详情模块立即购买,进入确认订单模块。确认订单模块提交订单后返回商品详情模块。同时通知下单成功。因此,上图中的入参提供了ConfirmComplete块。下图是TAConfirmOrderBusinessCategory.m#import"CTMediator+TAConfirmOrder.h"@implementationCTMediator(TAConfirmOrder)-(UIViewController*)confirmOrderViewControllerWithGoodsId:(NSString*)goodsIdgoodsName:(NSString*)goodsNameConfirmComplete:(dispatch_blockD_t)confirmComplete{NSMutable*中的实现=[[NSMutableDictionaryalloc]init];params[@"goodsId"]=goodsId;params[@"goodsName"]=goodsName;params[@"completeBlock"]=confirmComplete;return[selfperformTarget:@"TAConfirmOrder"action:@"ConfirmOrderViewController"params:paramsshouldCacheTarget:NO];}@endOK,TAConfirmOrderBusinessCategory实现了,我们看TAConfirmOrder模块,定义了一个Target_TAConfirmOrder如下图@interfaceTarget_TAConfirmOrder:NSObject-(UIViewController*)Action_ConfirmOrderViewController:(NSDictionary*)params;@end@implementationTarget_TAConfirmOrder-(UIViewController*)Action_ConfirmOrderViewController:(NSDictionary*)params{TAConfirmOrderViewController*confirmOrderVC=[[TAConfirmOrderViewControlleralloc]init];confirmId=parVC.goods商品ID"];confirmOrderVC.goodsName=params[@"goodsName"];confirmOrderVC.confirmComplete=params[@"completeBlock"];returnconfirmOrderVC;}@end既然TAConfirmOrderBusinessCategory和TAConfirmOrder是两个项目,category怎么调用Target_TAConfirmOrder呢?其实很简单。我想大部分看过这篇文章的人都知道,无非就是NSClassFromString、performSelector之类的方法,不知道的可以去看看源码,我这里没有贴出架构图,也没有解释原理,我只是贴出一部分,代码和说明是怎么实现的,为什么?其实组件化的原理很简单,somucheasi不如一开始就学习UITableView。我的Demo就是原理。如果还有不明白的可以自行google或者评论区提问。Target_ActionVSProtocol解决方案1.需要注册吗?Target_Action解决方案不需要注册。Protocol解决方案需要在启动时注册到CRProtocolManager。Target_Action很好的利用了runtime的特性,减少了注册的步骤,但是对于即将转Swift的同学来说有点尴尬。在上一篇文章提供的Protocol解决方案Demo中,向CRProtocolManager注册服务的是实例对象,而不是Class。这确实会导致内存常驻,但无害。熟悉runtime的同学应该都知道,第一次调用类或对象的方法时,会构建一个类对象,所以无论是将类对象注册到Class还是实例对象,一个实例占用的内存没有任何属性的对象非常小,与类对象无关。当然,你可能会问为什么你注册一个实例而不是一个类,因为它们几乎是一样的。注册的ServiceProvider实例对象在某些情况下可以记录一些状态。当然,这只是极少数情况。如果你真的想使用ServiceProvider作为单例对象,我还是强烈推荐注册Class。不过我觉得ServiceProvider需要注册中间件这件事逻辑上没有问题。不同的是可以保存也可以不保存。2.依赖Target_Action方案中的商品详情模块依赖TAConfirmOrderBusinessCategory组件获取订单确认模块。服务协议方案中的商品详情模块需要依赖CRConfirmOrderServiceProtocol通过CRProtocolManager组件获取提供服务的实例对象,确认订单模块也依赖CRConfirmOrderServiceProtocol注册服务。同时依赖于CRConfirmOrderServiceProtocol。实际上,CRConfirmOrderServiceProtocol应该是确认订单模块的一部分。它被隔离只是为了避免调用者直接引用实现者的需要。这种依赖关系应该体现在虚线而不是架构图中的实线。试想如果Target_Action方案不使用runtime,那么BusinessCategory也需要直接依赖Target。对CRConfirmOrderServiceProtocol的依赖也可以通过在运行时使用NSProtocolFromString来解决,但是一定程度的硬编码不够优雅。(顺便提一下,虽然runtime在某些特定场景下给我们的开发带来了一些意想不到的效果,但是runtime跳过了编译器检查,有时bug很难消除,所以还是慎用)另外,productdetails模块Protocol方案中也是依赖CRConfirmOrderServiceProtocol和CRProtocolManager,而Target_Action方案中商品详情模块只依赖TAConfirmOrderBusinessCategory。依赖关系如下图所示。Protocol方案横向依赖,Target_Action方案纵向依赖。Target_Action设计的比较好3.可读性,硬编码Target_Action在Category中将正则参数打包成一个字典,然后在Target将字典解包成正则参数,造成一定的硬编码,但在实际开发中,每个提供的category模块通常是一个人写的,所以影响很小,但是给其他人阅读代码带来了一些不便,而且即使是同一个人写Cagetory和Target的时候,也需要在两个项目中保持switchto0hardcoded在之前Target中定义的函数名的protocolscheme中,更具可读性。这里提一下Url注册方案。我觉得Url注册方案最大的问题就是硬编码多,可读性差,可维护性差,对文档的依赖度高,监督不断。文档更新。我想很多同学对此都有深刻的理解。各个项目最新版本的接口文档都比较详细和全面。随着版本的迭代更新,某个接口增加了一些字段,通常是后台开发忘记更新了。文档可能是因为比较忙,有同学甚至偷懒更新文档。通常这个时候,他们会通过QQ或者其他通讯工具告知客户端开发者增加了哪些字段,这些字段的含义是什么。等了很久,客户端开发者忘记了这个字段的意义或者其他开发者接手了。不知道这个字段是什么意思。首先,我翻了一下之前的聊天记录。如果找不到,我就去接口文档。该文档仍然是1.0版本。...我去。..总结以上3点,Target_Action更好。我们公司目前使用的是Target_Action方案。如果有打算转Swift语言开发的同学,我推荐Protocol方案。其实没有什么方案是完美的,具体采用还要结合自己的业务和开发人员的综合素质。如果你还拿不准,阿里开源了一个模块解耦框架BeeHive(协议注册),你可以离大厂近一点。Url注册方案的demo我就不提供了,因为它的可读性、可维护性以及常规传参的缺点让我放弃了,但是Url注册方案可以很好的解决服务端发送路由的bug,前提是你的模块要Native开发,开发一套H5(或者RN和Weex)来补充业务模块的划分。很多同学知道组件化,但是不知道怎么划分业务模块。我大致以京东App的一些业务为例。见下图。每个Module在组件化后都是一个独立的项目。可能很多项目中只有一个ViewController。这也是一种合理的划分,比如产品细节。很多模块(服装城、京东超市、环球购...)都会调用商品详情模块,将商品详情模块(服装城、京东超市、环球购...)中的业务强行给任何项目是不合理的,确认订单也是如此。组件化就是把业务垂直切开,具体到某个业务模块中的网络模块,数据库模块的切分是横切预览。我发现很多同学理解MVC的错误姿势,使得控制器非常臃肿,难以维护。我会在下一篇MVC写成Demo的时候了解自己,这个Demo会是一个比较大的业务模块(所以时间会长一些。毕竟白天要忙公司的项目,还有需要维护的几个个人项目)。在这个Demo中,职责分工会很明确,敬请期待。
