美文网首页
5. 中间件组件化

5. 中间件组件化

作者: LeeDev | 来源:发表于2019-08-15 15:42 被阅读0次

    在上篇文章我们讨论一下,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:&params 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:&params 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:&params 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:&params 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:&params 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. 中间件组件化

          本文链接:https://www.haomeiwen.com/subject/suvzjctx.html