中间件(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
相关推荐
Digitally5 小时前
如何将数据从 iPhone 传输到笔记本电脑
ios·电脑·iphone
卜及中5 小时前
【Redis/1-前置知识】分布式系统概论:架构、数据库与微服务
数据库·redis·架构
阿芯爱编程5 小时前
最长和谐子序列,滑动窗口
前端·javascript·面试
白玉cfc6 小时前
【iOS】cell的复用以及自定义cell
ios·cocoa·xcode
拉不动的猪7 小时前
JQ常规面试题
前端·javascript·面试
星释8 小时前
使用Appium在iOS上实现自动化
ios·appium·自动化
慌糖9 小时前
Spring Boot 分层架构与数据流转详解
spring boot·后端·架构
Digitally9 小时前
如何安全地准备 iPhone 以旧换新(分步说明)
安全·ios·iphone
二流小码农9 小时前
鸿蒙开发:一文了解桌面卡片
android·ios·harmonyos
每次的天空9 小时前
Android第十七次面试总结(Java数据结构)
android·java·面试