整体架构

casatwy组件化方案分为两种调用方式,远程调用和本地调用,对于两个不同的调用方式分别对应两个接口。

  • 远程调用通过AppDelegate代理方法传递到当前应用后,调用远程接口并在内部做一些处理,处理完成后会在远程接口内部调用本地接口,以实现本地调用为远程调用服务

  • 本地调用由performTarget:action:params:方法负责,但调用方一般不直接调用performTarget:方法CTMediator会对外提供明确参数和方法名的方法,在方法内部调用performTarget:方法和参数的转换。

16270478-00fb1f35705bb080

casatwy提出的组件化架构

架构设计思路

casatwy是通过CTMediator类实现组件化的,在此类中对外提供明确参数类型的接口,接口内部通过performTarget方法调用服务方组件的TargetAction。由于CTMediator类的调用是通过runtime主动发现服务的,所以服务方对此类是完全解耦的。

但如果CTMediator类对外提供的方法都放在此类中,将会对CTMediator造成极大的负担和代码量。解决方法就是对每个服务方组件创建一个CTMediatorCategory,并将对服务方的performTarget调用放在对应的Category中,这些Category都属于CTMediator中间件,从而实现了感官上的接口分离。

17270478-488134663e7c7744

casatwy组件化实现细节

对于服务方的组件来说,每个组件都提供一个或多个Target类,在Target类中声明Action方法。Target类是当前组件对外提供的一个“服务类”Target将当前组件中所有的服务都定义在里面,CTMediator通过runtime主动发现服务

Target中的所有Action方法,都只有一个字典参数,所以可以传递的参数很灵活,这也是casatwy提出的Model化的概念。在Action的方法实现中,对传进来的字典参数进行解析,再调用组件内部的类和方法。

架构分析

casatwy为我们提供了一个Demo,通过这个Demo可以很好的理解casatwy的设计思路,下面按照我的理解讲解一下这个Demo

18270478-106c9ecc613498bd

文件目录

打开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中定义的ModuleACategory,对外提供了一个获取控制器并跳转的功能,下面是代码实现。由于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采取的是加前缀的方式,从casatwyDemo中也可以看出,其组件ModuleATarget命名为Target_A,被调用的Action命名为Action_nativeFetchDetailViewController:

casatwy将类和方法的命名,都统一按照其功能做区分当做前缀,这样很好的将组件相关和组件内部代码进行了划分。