组件化中间件选型(二): CTMediator
关于中间件在组件化中间件选型(一):MGJRouter中已经介绍过了,直接介绍CTMediator
设计思路
CTMediator提供了三种API:远程app调用的入口、本地组件调用入口、释放某个target缓存,这里主要讨论本地组件调用入口。
所有组件都通过组件自带的Target-Action来响应,模块与模块的交互接口都被固话在了这一层,通过runtime机制获取到我们想要调用的Target,并通过Action获取到想要调动的选择子,主要用到的方法就是performSelector: withObject:。
应用场景:
模拟三个组件之间的交互方法,设置了两种交互的场景,一种是单纯的携带参数实现交互,另一种是回调参数。其中viewController是第一个组件,package2List、package1List分别是第二个组件和第三个组件,ViewController和package1List实现携带参数的界面跳转,ViewController和package2List实现携带参数的界面跳转,并回传参数。
携带参数实现交互
先查看一下CTMediator单例类里是如何写的,在- (id)performTarget:(NSString*)targetName action:(NSString*)actionName params:(NSDictionary*)params shouldCacheTarget:(BOOL)shouldCacheTarget 方法中,生成的对应target 类是以Target_开头的,

相应的Action方法是以Action_:开头的

所以我们需要借助runtime中用到的Target_中间类来处理相应的action操作

按照CTMediator中的源码,我们建立了Target_Package1ViewController类和Action_Package1ViewController:函数,以及创建CTMediator分类CTMediator+Package1。
其中CTMediator+Package1的主要作用是:生成CTMediator中需要的params,传递下一步处理函数的类名称和方法名称,并调用基类中的performTarget:(NSString*)targetName action:(NSString*)actionName params:(NSDictionary*)params shouldCacheTarget:(BOOL)shouldCacheTarget 方法实现runtime调用具体类中的指定方法。

Target_Package1ViewController的主要作用是:把从上一个组件中传递的参数对当前组件进行赋值操作,并返回我们想要的值

Package1ViewController:将上一个组件中传递过来的信息处理当下模块中的业务

在viewController中的调用:

直接调用CTMediator+Package1中的构造方法,并不会和package1List组件中的任何类产生耦合,整个交互过程都是黑箱操作。
回调参数
按照携带参数实现交互中的介绍,同样的我们建立了Target_Package2ViewController类和Action_Package2ViewController:函数,以及创建CTMediator分类CTMediator+Package2。
我们采用闭包的方式进行参数的回调,其中在CTMediator+Package2中把闭包当做参数进行传递

在Target_Package2ViewController中,我们进行参数的赋值操作

在Package2ViewController组件中,我们只要处理当前的业务逻辑,当需要触发回调操作的时候,调用闭包实现参数的回传。

在viewController的初始调用组件中,我们通过CTMediator+Package2调用和组件package2List中具体类交互的方法。

总结:整个组件和组件间的相互调用,其实是采用了工厂方法模式,用这种架构,可以批量注册不同的工厂类,生产出不同的产品。其中CTMediator+Package2和CTMediator+Package1是CTMediator的工厂类,并且这两个工厂创建了Target_Package2ViewController和Target_Package1ViewController两个产品,根据这两个产品中的方法,我们可以获得需要的类,实现交互的完全黑盒化操作,去耦合性操作。
相比蘑菇街的MGJRouter方案,CTMediaor方案有很多优点:
1、蘑菇街必须要在app启动时注册URL响应者
2、蘑菇街没有针对target层单独的处理,而是将调用耦合进了业务代码里
CTMediator的缺点:
1、param的key的两端一致性,才能将参数顺利传递到下一个组件的业务层
2、每一个组件都需要建立Target_ 类和 CTMediator扩展类,耦合性降低了但是需要多创建一些类别来处理具体业务