中间件(Mediator)模式的实现

当涉及到模块化和组件化开发时,中介者模式(Mediator Pattern)可以帮助解耦不同模块之间的依赖关系。它通过提供一个统一的接口,将模块之间的通信和交互逻辑集中在一个中间件类中,而不是直接依赖于具体的模块。

中介者模式OC实现类

objectivec 复制代码
//  YJHMediator.h
#import <Foundation/Foundation.h>

@interface YJHMediator : NSObject

+ (instancetype)sharedInstance;

// 远程App调用入口
- (id)performActionWithUrl:(NSURL *)url completion:(void(^)(NSDictionary *info))completion;

// 本地组件调用入口
- (id)performTarget:(NSString *)targetName action:(NSString *)actionName params:(NSDictionary *)params shouldCacheTarget:(BOOL)shouldCacheTarget;

- (void)releaseCachedTargetWithTargetName:(NSString *)targetName;

@end
// 复制代码
#import "YJHMediator.h"

// 定义 YJHMediator 的私有接口
@interface YJHMediator ()

// 存储缓存的目标对象的字典
@property (nonatomic, strong) NSMutableDictionary *cachedTarget;

@end

// 实现 YJHMediator 类
@implementation YJHMediator

#pragma mark - public methods

// 单例方法,返回共享的 YJHMediator 实例
+ (instancetype)sharedInstance
{
    static YJHMediator *mediator;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        mediator = [[YJHMediator alloc] init];
    });
    return mediator;
}

/*
 scheme://[target]/[action]?[params]
 
 url sample:
 aaa://targetA/actionB?id=1234
 */

// 根据 URL 执行对应的操作,并在完成时调用 completion 回调
- (id)performActionWithUrl:(NSURL *)url completion:(void (^)(NSDictionary *))completion
{
    // 创建参数字典
    NSMutableDictionary *params = [[NSMutableDictionary alloc] init];
    
    // 获取 URL 的查询部分
    NSString *urlString = [url query];
    
    // 遍历查询部分,将参数解析为键值对并保存到 params 字典中
    for (NSString *param in [urlString componentsSeparatedByString:@"&"]) {
        NSArray *elts = [param componentsSeparatedByString:@"="];
        if([elts count] < 2) continue;
        [params setObject:[elts lastObject] forKey:[elts firstObject]];
    }
    
    // 根据 URL 的路径获取 actionName
    NSString *actionName = [url.path stringByReplacingOccurrencesOfString:@"/" withString:@""];
    
    // 检查 actionName 是否以 "native" 开头,如果是则返回 NO
    if ([actionName hasPrefix:@"native"]) {
        return @(NO);
    }
    
    // 调用 performTarget:action:params:shouldCacheTarget: 方法执行目标操作,并返回结果
    id result = [self performTarget:url.host action:actionName params:params shouldCacheTarget:NO];
    
    // 执行 completion 回调,并传递结果字典
    if (completion) {
        if (result) {
            completion(@{@"result":result});
        } else {
            completion(nil);
        }
    }
    
    // 返回结果
    return result;
}

// 执行目标操作的方法
- (id)performTarget:(NSString *)targetName action:(NSString *)actionName params:(NSDictionary *)params shouldCacheTarget:(BOOL)shouldCacheTarget
{
    // 检查 targetName 和 actionName 是否为空
    if (!targetName || !actionName) {
        return nil;
    }
    
    // 构造目标类名
    NSString *targetClassString = [NSString stringWithFormat:@"Target_%@", targetName];
    
    // 构造方法名
    NSString *actionString = [NSString stringWithFormat:@"IMP_%@", actionName];
    
    // 如果 params 不为空,将冒号添加到 actionString 后面
    if (params != nil) {
        actionString = [actionString stringByAppendingString:@":"];
    }
    
    id target = nil;
    
    // 使用同步锁保证多线程安全
    @synchronized (self) {
        target = self.cachedTarget[targetClassString];
    }
    
    // 如果缓存中没有目标对象,则根据 targetClassString 创建目标对象
    if (target == nil) {
        Class targetClass = NSClassFromString(targetClassString);
        target = [[targetClass alloc] init];
    }
    
    SEL action = NSSelectorFromString(actionString);
    
    // 如果目标对象为空,直接返回空
    if (target == nil) {
        return nil;
    }
    
    // 如果 shouldCacheTarget 为 YES,则将目标对象缓存起来
    if (shouldCacheTarget) {
        @synchronized (self) {
            self.cachedTarget[targetClassString] = target;
        }
    }
    
    // 检查目标对象是否响应 action 方法,如果是,则执行安全的方法调用
    if ([target respondsToSelector:action]) {
        return [self safePerformAction:action target:target params:params];
    } else {
        // 如果目标对象不响应 action 方法,则尝试调用目标对象的 notFound: 方法进行统一处理
        SEL action = NSSelectorFromString(@"notFound:");
        if ([target respondsToSelector:action]) {
            return [self safePerformAction:action target:target params:params];
        } else {
            // 如果目标对象也没有 notFound: 方法,则直接返回空
            return nil;
        }
    }
}

// 安全的方法调用,避免因为方法签名不匹配导致的崩溃
- (id)safePerformAction:(SEL)action target:(NSObject *)target params:(NSDictionary *)params
{
    NSMethodSignature *methodSignature = [target methodSignatureForSelector:action];
    
    if (methodSignature == nil) {
        return nil;
    }
    
    const char *retType = [methodSignature methodReturnType];
    
    if (strcmp(retType, @encode(void)) == 0) {
        // 返回值为 void 的方法调用
        NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSignature];
        [invocation setSelector:action];
        [invocation setTarget:target];
        
        if (params) {
            [invocation setArgument:&params atIndex:2];
        }
        
        [invocation invoke];
        return nil;
    } else if (strcmp(retType, @encode(NSInteger)) == 0) {
        // 返回值为 NSInteger 的方法调用
        NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSignature];
        [invocation setSelector:action];
        [invocation setTarget:target];
        
        if (params) {
            [invocation setArgument:&params atIndex:2];
        }
        
        [invocation invoke];
        
        NSInteger result = 0;
        [invocation getReturnValue:&result];
        return @(result);
    } else {
        // 其他类型的返回值
        NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSignature];
        [invocation setSelector:action];
        [invocation setTarget:target];
        
        if (params) {
            [invocation setArgument:&params atIndex:2];
        }
        
        [invocation invoke];
        
        id result = nil;
        [invocation getReturnValue:&result];
        return result;
    }
}

#pragma mark - getters and setters

- (NSMutableDictionary *)cachedTarget
{
    if (!_cachedTarget) {
        _cachedTarget = [[NSMutableDictionary alloc] init];
    }
    return _cachedTarget;
}

@end

如何使用

使用 YJHMediator 类可以实现对象之间的解耦和相互调用。下面是使用 YJHMediator 的一般步骤:

  1. 创建目标对象:首先,需要创建一个或多个目标对象,这些对象包含想要调用的方法。每个目标对象都应该是一个独立的类,并遵循特定的命名规则。

  2. 实现目标对象的方法:在每个目标对象类中,实现想要调用的方法。确保方法的命名以 "IMP_" 开头,例如 IMP_doSomething:。方法的参数应该是一个 NSDictionary 类型的参数。

  3. 调用目标对象方法:在需要调用目标对象方法的地方,使用 YJHMediatorperformTarget:action:params:shouldCacheTarget: 方法来调用指定目标对象的指定方法。传入目标对象名称、方法名称和参数。根据需要,可以选择是否缓存目标对象。

  4. 完成调用并处理结果:根据你的需求,可以在调用方法后处理返回的结果。

下面是一个简单的示例,演示如何使用 YJHMediator

objective-c 复制代码
// 创建目标对象类
@interface Target_A : NSObject
- (void)IMP_doSomething:(NSDictionary *)params;
@end

@implementation Target_A
- (void)IMP_doSomething:(NSDictionary *)params {
    NSString *message = params[@"message"];
    NSLog(@"Target A is doing something with message: %@", message);
}
@end

// 在适当的地方调用目标对象的方法
NSDictionary *params = @{@"message": @"Hello, XCMediator!"};
[[YJHMediator sharedInstance] performTarget:@"A" action:@"doSomething" params:params shouldCacheTarget:NO];

// 输出结果:Target A is doing something with message: Hello, XCMediator!

在上面的示例中,我们创建了一个名为 Target_A 的目标对象类,并实现了 IMP_doSomething: 方法。然后,我们通过 YJHMediatorperformTarget:action:params:shouldCacheTarget: 方法调用了 Target_A 类的 doSomething 方法,并传入了一个包含消息的参数字典。最后,Target_AdoSomething 方法被调用,并输出相应的日志信息。

请注意,这只是一个简单的示例,实际使用中可能涉及更复杂的业务逻辑和多个目标对象。确保根据你的需求适当地组织和管理目标对象和方法,并遵循命名规则以使 YJHMediator 能够正确识别和调用它们。

示例

  1. 创建目标对象:首先,需要创建一个或多个目标对象,这些对象包含想要调用的方法。每个目标对象都应该是一个独立的类,并遵循特定的命名规则。
  2. 实现目标对象的方法:在每个目标对象类中,实现想要调用的方法。确保方法的命名以 "IMP_" 开头,例如 IMP_doSomething:。方法的参数应该是一个 NSDictionary 类型的参数。
objectivec 复制代码
//  Target_TTHomeMoudle.h

#import <UIKit/UIKit.h>

typedef void(^TTSearchPresentDidClickBlock)(long long, NSString *);

@interface Target_TTHomeMoudle : NSObject

- (UIViewController *)IMP_TTSearchRoomViewController:(NSDictionary *)dict;

@end
ini 复制代码
//  Target_TTHomeMoudle.m

#import "Target_TTHomeMoudle.h"
#import "TTSearchRoomViewController.h"

@implementation Target_TTHomeMoudle

- (UIViewController *)IMP_TTSearchRoomViewController:(NSDictionary *)dict {

    TTSearchRoomViewController *searchVC = [[TTSearchRoomViewController alloc] init];

    if ([dict.allKeys containsObject:@"isPresent"]) {

        searchVC.isPresent = dict[@"isPresent"];

    }

    if ([dict.allKeys containsObject:@"isInvite"]) {

        searchVC.isInvite = dict[@"isInvite"];

    }

    if ([dict.allKeys containsObject:@"isHallSearch"]) {

        searchVC.isHallSearch = dict[@"isHallSearch"];

    }

    //允许显示历史记录

    if ([dict.allKeys containsObject:@"showHistoryRecord"]) {

        searchVC.showHistoryRecord = [dict[@"showHistoryRecord"] boolValue];

    }

    if ([dict.allKeys containsObject:@"block"]) {

        searchVC.searchPresentDidClickBlock = dict[@"block"];

    }

    if ([dict.allKeys containsObject:@"dismissBlock"]) {

        searchVC.dismissAndDidClickPersonBlcok = dict[@"dismissBlock"];

    }

    if ([dict.allKeys containsObject:@"enterRoomBlock"]) {

        searchVC.enterRoomHandler = dict[@"enterRoomBlock"];

    }
    return searchVC;
}

@end
  1. 调用目标对象方法:在需要调用目标对象方法的地方,使用 YJHMediatorperformTarget:action:params:shouldCacheTarget: 方法来调用指定目标对象的指定方法。传入目标对象名称、方法名称和参数。根据需要,可以选择是否缓存目标对象。
  2. 完成调用并处理结果:根据你的需求,可以在调用方法后处理返回的结果。

例如创建一个YJHMediator的分类定义一个方法使用 YJHMediatorperformTarget:action:params:shouldCacheTarget: 方法来调用指定目标对象的指定方法

objectivec 复制代码
//  YJHMediator+TTHomeMoudle.h
#import "YJHMediator.h"
#import <UIKit/UIKit.h>

@interface YJHMediator (TTHomeMoudle)

/**
 搜索控制器
 */

- (UIViewController *)homeMethod_CallName_searchRoomController:(BOOL)isPresent block:(id)block;

@end
objectivec 复制代码
//  YJHMediator+TTHomeMoudle.m

#import "YJHMediator+TTHomeMoudle.h"

@implementation YJHMediator (TTHomeMoudle)

/**
 搜索控制器
 */

- (UIViewController *)homeMethod_CallName_searchRoomController:(BOOL)isPresent block:(id)block {

    NSDictionary *params = @{@"isPresent" : @(isPresent), @"block" : block};

    return [self performTarget:@"TTHomeMoudle" action:@"TTSearchRoomViewController" params:params shouldCacheTarget:YES];

}

@end
相关推荐
ThisIsClark1 小时前
【后端面试总结】深入解析进程和线程的区别
java·jvm·面试
9527华安2 小时前
FPGA多路MIPI转FPD-Link视频缩放拼接显示,基于IMX327+FPD953架构,提供2套工程源码和技术支持
fpga开发·架构·音视频
/**书香门第*/2 小时前
Laya ios接入goole广告,搭建环境 1
ios
测试19982 小时前
外包干了2年,技术退步明显....
自动化测试·软件测试·python·功能测试·测试工具·面试·职场和发展
Aphasia3113 小时前
一次搞懂 JS 对象转换,从此告别类型错误!
javascript·面试
GISer_Jing5 小时前
2025年前端面试热门题目——HTML|CSS|Javascript|TS知识
前端·javascript·面试·html
上海运维Q先生6 小时前
面试题整理14----kube-proxy有什么作用
运维·面试·kubernetes
开发者每周简报7 小时前
求职市场变化
人工智能·面试·职场和发展
三桥彭于晏8 小时前
B/S 跟C/S架构的区别
架构
wakangda8 小时前
React Native 集成 iOS 原生功能
react native·ios·cocoa