一、基本概括

讲解

在组件化之前,app都是在一个工程里开发的,开发的人员也是比较少的,业务发展也不是非常快,项目中不引用组件化开发也是合适的。但是当开发人员越来越多,代码量也就越来越多,业务也就越来越复杂,这时候单一的开发模式会显露出一些弊端:

  • 容易出现冲突(使用xib)
  • 耦合较严重(代码没有明确的约束,代码臃肿)
  • 开发效率不够高

为了解决这些问题,于是出现了组件化开发的策略,能带来好处如下:

  • 加快编译速度
  • 自由选择开发姿势(MVC/MVVM/FRP)
  • 方便有针对性测试
  • 提高开发效率

今天我们讲解一下组件化开发,也是自己项目中使用到的一种方式Target-Action方式。主要是基于Mediator模式和Target-Action,中间采用了Runtime来完成调用。这套组件化将远程和本地应用调用做了拆分,而且是本地调用是为远程调用提供服务。

下面是target-action的工作图:

 调用方式:

本地组件调用:本地组件A在一处调用[[CTMediator sharedInstance] performTarget:targetName action:actionName params:@{...}]向CTMediator发起了跨组件调用,CTMediator根据发送过来的target和action,然后经过OC的runtime机制转为target实例以及action,最后调用到目标业务提供的逻辑,完成要求.

远程应用的调取:远程应用是通过openURL的方式,由iOS 系统根据info.plist里的scheme配置用来可以找到响应的URL的应用,应用直接通过AppDelegate接收到URL之后,调用了CTMediator的OpenURL方法将接收到的信息传入进去.当然,CTMediator也可以用CTMediator的openURL:options:方式顺便将option也接收,这取决于是否包含了option数据,传入URL之后,CTMediator进行解析URL,将请求的路由到相对应的target-action中,随后的过程就变成了上面的本地应用调用过程了,最终完成了响应.

对应着如下:

 

 组件化方案中的去Model化设计

组件化调用的时候,应该设计是要对参数做去Model化的。如果组件间调用不对的参数做去Model化的设计,就会导致有问题-业务形式上被组件化了,而实际上是没有独立。

如果模块A和模块B采用了Model化的方案,调用方法时传递的参数就是一个对象,因此使用对象化的参数无论是面向接口,带来的结果就是业务模块形式上被组件化了,但是实质上还是没有被独立。

在跨模块场景中,参数最好还是使用去Model化的的方式去传递,在苹果开发中,也就是以字典的形式去传递。这样就可以做到只有调用方依赖mediator,而响应方是不需要依赖mediator。

在去Model的组件化的方案中,影响效率的总有两个:

  • 调用方如何知道接收方需要哪些参数呢?
  • 调用方怎么知道有哪些target可以被调用呢?

其实后面问题,无论是不是具有去Model都有这个问题,但是为什么要一起说了呢,因为下面提供一种方案来将出现的两个问题一起解决。

 

解决方案使用category

该方案基于是mediator和target-action模式组件化开发,通过运行时完成调用。mediator维护着若干个category,一个category对应着一个target,而一个target可以包含着多个action。

我们也可以这样理解:一个业务组件包括了一个category组件,这个category中有个mediator的category,而category中有一个target,这个target对应着此业务组件。target中又有若干个接口方法,用来其他业务来获取该业务组件中的业务。

category本身是一种组合模式,根据不同的分类提供不同方法,此时每个组件都是一个分类。

复制代码
if (indexPath.row == 0) {         UIViewController *viewController = [[CTMediator sharedInstance] CTMediator_viewControllerForDetail];          // 获得view controller之后,在这种场景下,到底push还是present,其实是要由使用者决定的,mediator只要给出view controller的实例就好了        [self presentViewController:viewController animated:YES completion:nil];     }      if (indexPath.row == 1) {         UIViewController *viewController = [[CTMediator sharedInstance] CTMediator_viewControllerForDetail];         [self.navigationController pushViewController:viewController animated:YES];     }      if (indexPath.row == 2) {         // 这种场景下,很明显是需要被present的,所以不必返回实例,mediator直接present了        [[CTMediator sharedInstance] CTMediator_presentImage:[UIImage imageNamed:@"image"]];     }      if (indexPath.row == 3) {         // 这种场景下,参数有问题,因此需要在流程中做好处理        [[CTMediator sharedInstance] CTMediator_presentImage:nil];     }      if (indexPath.row == 4) {         [[CTMediator sharedInstance] CTMediator_showAlertWithMessage:@"casa" cancelAction:nil confirmAction:^(NSDictionary *info) {             // 做你想做的事            NSLog(@"%@", info);         }];     }