整体架构
casatwy组件化方案分为两种调用方式,远程调用和本地调用,对于两个不同的调用方式分别对应两个接口。
远程调用通过
AppDelegate
代理方法传递到当前应用后,调用远程接口并在内部做一些处理,处理完成后会在远程接口内部调用本地接口,以实现本地调用为远程调用服务。本地调用由
performTarget:action:params:
方法负责,但调用方一般不直接调用performTarget:
方法。CTMediator
会对外提供明确参数和方法名的方法,在方法内部调用performTarget:
方法和参数的转换。
casatwy提出的组件化架构
架构设计思路
casatwy是通过CTMediator
类实现组件化的,在此类中对外提供明确参数类型的接口,接口内部通过performTarget
方法调用服务方组件的Target
、Action
。由于CTMediator
类的调用是通过runtime
主动发现服务的,所以服务方对此类是完全解耦的。
但如果CTMediator
类对外提供的方法都放在此类中,将会对CTMediator
造成极大的负担和代码量。解决方法就是对每个服务方组件创建一个CTMediator
的Category
,并将对服务方的performTarget
调用放在对应的Category
中,这些Category
都属于CTMediator
中间件,从而实现了感官上的接口分离。
casatwy组件化实现细节
对于服务方的组件来说,每个组件都提供一个或多个Target
类,在Target
类中声明Action
方法。Target
类是当前组件对外提供的一个“服务类”,Target
将当前组件中所有的服务都定义在里面,CTMediator
通过runtime
主动发现服务。
在Target
中的所有Action
方法,都只有一个字典参数,所以可以传递的参数很灵活,这也是casatwy提出的去Model
化的概念。在Action
的方法实现中,对传进来的字典参数进行解析,再调用组件内部的类和方法。
架构分析
casatwy为我们提供了一个,通过这个Demo
可以很好的理解casatwy的设计思路,下面按照我的理解讲解一下这个Demo
。
文件目录
打开Demo
后可以看到文件目录非常清楚,在上图中用蓝框框出来的就是中间件部分,红框框出来的就是业务组件部分。我对每个文件夹做了一个简单的注释,包含了其在架构中的职责。
在CTMediator
中定义远程调用和本地调用的两个方法,其他业务相关的调用由Category
完成。
1 2 3 4 | // 远程App调用入口 - (id)performActionWithUrl:(NSURL *)url completion:(void(^)(NSDictionary *info))completion; // 本地组件调用入口 - (id)performTarget:(NSString *)targetName action:(NSString *)actionName params:(NSDictionary *)params; |
在CTMediator
中定义的ModuleA
的Category
,对外提供了一个获取控制器并跳转的功能,下面是代码实现。由于casatwy的方案中使用performTarget
的方式进行调用,所以涉及到很多硬编码字符串的问题,casatwy采取定义常量字符串来解决这个问题,这样管理也更方便。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | #import "CTMediator+CTMediatorModuleAActions.h"
NSString * const kCTMediatorTargetA = @"A"; NSString * const kCTMediatorActionNativFetchDetailViewController = @"nativeFetchDetailViewController";
@implementation CTMediator (CTMediatorModuleAActions)
- (UIViewController *)CTMediator_viewControllerForDetail { UIViewController *viewController = [self performTarget:kCTMediatorTargetA action:kCTMediatorActionNativFetchDetailViewController params:@{ @"key":@"value"}]; if ([viewController isKindOfClass:[UIViewController class]]) { // view controller 交付出去之后,可以由外界选择是push还是present return viewController; } else { // 这里处理异常场景,具体如何处理取决于产品 return [[UIViewController alloc] init]; } } |
下面是ModuleA
组件中提供的服务,被定义在Target_A
类中,这些服务可以被CTMediator
通过runtime
的方式调用,这个过程就叫做发现服务。
我们发现,在这个方法中其实做了参数处理和内部调用的功能,这样就可以保证组件内部的业务不受外部影响,对内部业务没有侵入性。
1 2 3 4 5 6 | - (UIViewController *)Action_nativeFetchDetailViewController:(NSDictionary *)params { // 对传过来的字典参数进行解析,并调用ModuleA内部的代码 DemoModuleADetailViewController *viewController = [[DemoModuleADetailViewController alloc] init]; viewController.valueLabel.text = params[@"key"]; return viewController; } |
命名规范
在大型项目中代码量比较大,需要避免命名冲突的问题。对于这个问题casatwy采取的是加前缀的方式,从casatwy的Demo
中也可以看出,其组件ModuleA
的Target
命名为Target_A
,被调用的Action
命名为Action_nativeFetchDetailViewController:
。
casatwy将类和方法的命名,都统一按照其功能做区分当做前缀,这样很好的将组件相关和组件内部代码进行了划分。