5. 中间件组件化

在上篇文章我们讨论一下,url方式和缺点,我们现在来讨论通过,中间件来实现解耦。 主要是参考 CTMediator,来实现我们的组件化。笔者考虑到CTMediator 当做一个单例来处理(内部有个target缓存),我不太想这样处理,我觉得Mediator 就是一个 工具类,通过传递 组件的target 和 sel,其实就是一个加方法 就可以的。

中间件代码

这个中间件,其实就是一个动态解析的过程,把哪些组件的使用放到动态解析中,这里就不用注册了。

下面是笔者改造后的中间件,主要有三部分组成

  • target: 目标组件,我们一边抽离一个工具类(使用 门面模式)
  • action: 调用方法
  • param: 传递参数,和使用回调

代码如下

//传递回调值
typedef void(^ZLMediatorCallBack)(id param);
extern NSString * const ZLMediatorCallBackKey; //可以放在 param 中

@interface ZLMediator : NSObject

// 本地组件调用入口
+ (id)Mediator_PerformTargetName:(NSString *)targetName
                      actionName:(NSString *)actionName
                          params:(NSDictionary *)params;


@end



NSString * const ZLMediatorCallBackKey = @"ZLMediatorCallBackKey";
@implementation ZLMediator
// 本地组件调用入口
+ (id)Mediator_PerformTargetName:(NSString *)targetName actionName:(NSString *)actionName params:(NSDictionary *)params {
    
    Class targetClass = targetName.length > 0 ? NSClassFromString(targetName) : nil;
    NSString * msg = [NSString stringWithFormat:@"不存在Target类名为:%@",targetName];
    NSAssert(targetClass, msg);
    
    SEL action = actionName.length > 0 ?  NSSelectorFromString(actionName) : nil;
    msg = [NSString stringWithFormat:@"Target_%@ 不存在 action为:%@",targetName,actionName];
    NSAssert(action, msg);
    
    if ([targetClass respondsToSelector:action]) {
        return [self SafePerformAction:action target:targetClass params:params];
    } else {
        msg = [NSString stringWithFormat:@"Target_%@ 不能响应 action_%@",targetName,actionName];
        NSAssert(0, msg);
    }
    return nil;
}


+ (id)SafePerformAction:(SEL)action target:(id)target params:(NSDictionary *)params {
    
    NSMethodSignature* methodSig = [target methodSignatureForSelector:action];
    if(methodSig == nil) {
        return nil;
    }
    const char* retType = [methodSig methodReturnType];
    
    if (strcmp(retType, @encode(void)) == 0) {
        NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSig];
        [invocation setArgument:¶ms atIndex:2];
        [invocation setSelector:action];
        [invocation setTarget:target];
        [invocation invoke];
        return nil;
    }
    
    if (strcmp(retType, @encode(NSInteger)) == 0) {
        NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSig];
        [invocation setArgument:¶ms atIndex:2];
        [invocation setSelector:action];
        [invocation setTarget:target];
        [invocation invoke];
        NSInteger result = 0;
        [invocation getReturnValue:&result];
        return @(result);
    }
    
    if (strcmp(retType, @encode(BOOL)) == 0) {
        NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSig];
        [invocation setArgument:¶ms atIndex:2];
        [invocation setSelector:action];
        [invocation setTarget:target];
        [invocation invoke];
        BOOL result = 0;
        [invocation getReturnValue:&result];
        return @(result);
    }
    
    if (strcmp(retType, @encode(CGFloat)) == 0) {
        NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSig];
        [invocation setArgument:¶ms atIndex:2];
        [invocation setSelector:action];
        [invocation setTarget:target];
        [invocation invoke];
        CGFloat result = 0;
        [invocation getReturnValue:&result];
        return @(result);
    }
    
    if (strcmp(retType, @encode(NSUInteger)) == 0) {
        NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSig];
        [invocation setArgument:¶ms atIndex:2];
        [invocation setSelector:action];
        [invocation setTarget:target];
        [invocation invoke];
        NSUInteger result = 0;
        [invocation getReturnValue:&result];
        return @(result);
    }
    
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
    return [target performSelector:action withObject:params];
#pragma clang diagnostic pop
}

@end

方便使用中间件

1.使用门面模式来暴露组件的使用

@interface Detail_Target : NSObject
+ (void)Target_showWithParam:(NSDictionary *)param;
@end

// 门面模式 定义方法
@implementation Detail_Target
+ (void)Target_showWithParam:(NSDictionary *)param {
    DetailComposite2 * detail = [[DetailComposite2 alloc] init];
    detail.oneId = param[@"id"];
    detail.name  = param[@"name"];
    // 执行组件的方法
    [detail showComposite];
}
@end

然后使用就可以

[ZLMediator Mediator_PerformTargetName:@"Detail_Target"
                                actionName:@"Target_showWithParam:"
                                    params:@{@"id":@"1", @"name":@"leeDev"}];
//打印出 showComposite2 _ id = 1 ; name = leeDev

Mediator 扩展

但是这样传递参数还是比较麻烦,所用我们可以使用 Media category 来简化我们的调用,让使用者更加明确

@interface ZLMediator (Detail)
//直接把 target 和 sel 和param 给屏蔽了,只给外界暴露 简单的接口
+ (void) detailShowWithId:(NSString *)id name:(NSString *)name;
@end

@implementation ZLMediator (Detail)

//直接把 target 和 sel 和param 给屏蔽了,只给外界暴露 简单的接口
+ (void) detailShowWithId:(NSString *)id name:(NSString *)name {
    NSDictionary * param = @{@"id":id, @"name":name};
    [ZLMediator Mediator_PerformTargetName:@"Detail_Target"
                                actionName:@"Target_showWithParam:"
                                    params:param];
}

@end

测试和使用

[ZLMediator detailShowWithId:@"10" name:@"leeDev"];
// 打印出 showComposite2 _ id = 10 ; name = leeDev

显然相当于上一种方法直接调用,这个方法要简单明确多了,直接屏蔽了 target 和 sel 和param.

优缺点

相对于蘑菇街的路由和协议方式的架构,这个方式要强大多了

  • 可以传递任意值
  • 不需要注册,浪费内存
  • 可以通过Mediator扩展,来定义更加清晰的接口给外界使用。

你可能感兴趣的:(5. 中间件组件化)