美文网首页iOS/MacOS开发
『iOS』如何设计一个好的插件(策略模式)

『iOS』如何设计一个好的插件(策略模式)

作者: butterflyer | 来源:发表于2021-09-09 20:45 被阅读0次

如何为设计一个可以好的插件库
我觉得,一个好的插件库,首先必须不能跟其他组件库耦合,那么该如何设计呢?
我觉得如果要设计好这个东西,必不可少的那就是协议,恰当的来说是使用了策略模式。
下面就用为flutter设计的插件来说一下,应该怎么来做。
flutter与原生之间的交互式用channel来实现的,当然可以不用太在意这些东西,看思路就好。

+ (void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar>*)registrar {
    FlutterMethodChannel* channel = [FlutterMethodChannel
                                     methodChannelWithName:@"cn.com.zhaopin.flutter_base_plugin"
                                     binaryMessenger:[registrar messenger]];
    FlutterBasePlugin* instance = [[FlutterBasePlugin alloc] init];
    [registrar addMethodCallDelegate:instance channel:channel];
}

比如我现在要实现几个功能,分别是埋点上报,错误捕获,图片浏览,图片选择。
因为通过channel来实现交互,然后通过handleMethodCall方法来下发flutter调用的方法。
最简单的方法,我们可以在FlutterBasePlugin中,简单的通过if else在handleMethodCall方法中实现。当然不会采用那种方式。
这里可以利用字典来设计一个key:value对应的方法表。

- (NSDictionary *)strategyMapDic {
    if (nil == _strategyMapDic) {
        _strategyMapDic = @{
            @"analyticsReport": @"ZPFlutterBaseTrackStrategy",              // 埋点上报
            @"catchException": @"ZPFlutterBaseCatcherStrategy",             // 错误捕获
            @"browsePic": @"ZPFlutterBaseImageBrowserStrategy",             // 图片浏览
            @"pickPhoto": @"ZPFlutterBaseImagePickerStrategy",              // 图片选择
            @"nativeDataMethod": @"ZPFlutterBaseNativeDataStrategy",        // 获取原生数据
        };
    }
    return _strategyMapDic;
}

ZPFlutterBaseTrackStrategy, ZPFlutterBaseCatcherStrategy, ZPFlutterBaseImageBrowserStrategy, ZPFlutterBaseImagePickerStrategy 这几个类,分别对应着不同的策略中转类。

- (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result {
        
    NSString *className = [self.strategyMapDic objectForKey:call.method];
    if (className.length > 0) {
        Class cls = NSClassFromString(className);
        if (cls) {
            id<ZPFlutterBaseStrategyProtocol> strategy = [[cls alloc] init];
            ZPFlutterBaseContext *context = [[ZPFlutterBaseContext alloc] initWithStrategy:strategy];
            [context executeStrategyWithArguments:call.arguments completion:^(NSString * _Nonnull jsonString) {
                result(jsonString);
            }];
            return;
        }
    }
    
    NSString *jsonString = [ZhaopinFlutterBaseManager formatFailureResultWithData:@{} code:@"-2" errorMsg:@"未找到方法实现"];
    result(jsonString);
}

这里需要一个中转类和一个中转协议,把flutter下发的方法,分别转发出去。仔细想了一下,这里用策略模式是很合适的。

/// 策略协议
@protocol ZPFlutterBaseStrategyProtocol <NSObject>

@required

/// 埋点查找实现类并调用
/// @param arguments flutter 参数
/// @param completion 调用完成回调
- (void)arguments:(NSDictionary *)arguments completion:(void (^)(id result))completion;

@end

@interface ZPFlutterBaseContext()

@property(nonatomic, strong) id<ZPFlutterBaseStrategyProtocol> strategy;


@end

@implementation ZPFlutterBaseContext


/// 初始化
/// @param strategy 策略实现
- (instancetype)initWithStrategy:(id<ZPFlutterBaseStrategyProtocol>) strategy {
    self = [super init];
    if (self) {
        self.strategy = strategy;
    }
    return self;
}

/// 执行策略
/// @param arguments 参数
/// @param completion 回调
- (void)executeStrategyWithArguments:(NSDictionary *)arguments completion:(void (^)(id result))completion {
    if (self.strategy && [self.strategy respondsToSelector:@selector(arguments:completion:)]) {
        [self.strategy arguments:arguments completion:completion];
    }
}

@end

当然,字典列表对应实现的这几个策略类,ZPFlutterBaseTrackStrategy, ZPFlutterBaseCatcherStrategy, ZPFlutterBaseImageBrowserStrategy, ZPFlutterBaseImagePickerStrategy 都实现了ZPFlutterBaseStrategyProtocol这个协议,所以通过[self.strategy arguments:arguments completion:completion];可以执行到每个类中的方法。

以ZPFlutterBaseCatcherStrategy举例。


@interface ZPFlutterBaseCatcherStrategy : NSObject<ZPFlutterBaseStrategyProtocol>

@end
@implementation ZPFlutterBaseCatcherStrategy


- (void)arguments:(NSDictionary *)arguments completion:(void (^)(id result))completion {
    if (ZP_F_BASE_IS_DICTIONARY(arguments)) {
        NSString *exceptionInfo = ZP_F_BASE_IS_STRING(arguments[@"info"]) ? arguments[@"info"] : @"";
        NSDictionary *context = ZP_F_BASE_IS_DICTIONARY(arguments[@"context"]) ? arguments[@"context"] : nil;
        if (exceptionInfo.length > 0) {
            Class cls = [[ZhaopinFlutterBaseManager sharedInstance] getClassWithKey:ZP_F_BASE_CARCHER_KEY];
            // 验证是否实现 ZhaopinFlutterBaseProtocol 协议
            if (ZP_F_BASE_IS_PROTOCOL(cls, @protocol(ZPFlutterBaseCatcherProtocol))) {
                [cls catchException:exceptionInfo context: context];
                if (completion) {
                    NSString *jsonString = [ZhaopinFlutterBaseManager formatSuccessResultWithData:@{}];
                    completion(jsonString);
                }
                return;
            }
        }
    }
    if (completion) {
        NSString *jsonString = [ZhaopinFlutterBaseManager formatFailureResultWithData:@{} code:@"-1" errorMsg:@"参数解析失败"];
        completion(jsonString);
    }
}

@end

到目前为止,flutter_base_plugin这个组件库中设计方法已经说完了,那么来考虑下,如何实现插拔,如何解耦。
秉承着注册哪个用哪个,我们同样可以使用字典来解决。
在组件库中,暴露出存取方法。

/// 获取实现类
/// @param key 插件约定 key
- (Class)getClassWithKey:(NSString *)key;

/// 注册实现类
/// @param key 插件约定 key
/// @param baseClass 实现类必须实现对应协议
- (void)registerClassWithKey:(NSString *)key class:(Class) baseClass;

看上面的ZPFlutterBaseCatcherStrategy中的实现
通过getkey方法,拿到别的组件中注册的实现类。

Class cls = [[ZhaopinFlutterBaseManager sharedInstance] getClassWithKey:ZP_F_BASE_CARCHER_KEY];

哪里需要用我们就在哪里注册

        [FlutterBasePlugin registerClassWithKey:ZP_F_BASE_CARCHER_KEY class:NSClassFromString(@"ZPMFlutterExceptionCatcher")];

ZPMFlutterExceptionCatcher这个类,就是最终的实现类。

@interface ZPMFlutterExceptionCatcher : NSObject <ZPFlutterBaseCatcherProtocol>
+ (void)catchException:(NSString *)exceptionInfo context:(NSDictionary *)context;
@end
@implementation ZPMFlutterExceptionCatcher
+ (void)catchException:(NSString *)exceptionInfo context:(NSDictionary *)context {
    NSString *crashReason = [NSString stringWithFormat:@"%@", exceptionInfo];

    [Bugly reportException:[[NSException alloc] initWithName:@"FlutterException" reason:crashReason userInfo:@{}]];
}
@end

整个文件的结构如下


image.png

相关文章

  • 『iOS』如何设计一个好的插件(策略模式)

    如何为设计一个可以好的插件库我觉得,一个好的插件库,首先必须不能跟其他组件库耦合,那么该如何设计呢?我觉得如果要设...

  • 设计模式

    iOS设计模式(5)策略模式 iOS适配器设计模式其实就是对某个控件上的各个部分,用一个model来统一赋值,而在...

  • iOS好文备忘录

    iOS github集锦 告别2016迎接2017,分享一些第三方插件 设计模式 iOS设计模式(代码分析系列2:...

  • 策略模式

    参考资料:漫话:如何给女朋友解释什么是策略模式? 设计模式之策略模式(实例+Demo) Java设计模式(8)——...

  • 2020-04-16

    iOS设计模式-策略模式 面向对象的设计模式中,我们可以把相关的算法分离为不同的类,成为策略。与这种做法相关的一种...

  • 《iOS开发》--------常用的设计模式

    关于iOS开发中的设计模式,当下有集中最常用的设计模式:代理模式、观察者模式、MVC模式、单例模式、策略模式、工厂...

  • 策略模式(详解)

    策略模式(来自HeadFirst设计模式) 今天看了 Head First 设计模式的第一个模式,居然是策略模式,...

  • 策略模式 2018-11-04

    设计模式之策略模式 官方说明设计模式的3个角色: 环境角色:context , 持有一个策略的引用 抽象策略角色,...

  • IOS策略模式和多态

    策略模式是一种常见的软件设计模式,这里简单得介绍一下策略模式并用IOS简单实现一下。所谓的策略模式,顾名思义是要采...

  • iOS设计模式(3)适配器模式

    设计模式系列文章 《iOS设计模式(1)简单工厂模式》《iOS设计模式(2)工厂模式》《iOS设计模式(4)抽象工...

网友评论

    本文标题:『iOS』如何设计一个好的插件(策略模式)

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