4. IOS 组件化(蘑菇街的路由+协议式)

为了研究组件化,我们主要是讨论 蘑菇街的路由+协议式中间件

讨论第一种方式,并参考 蘑菇街IOS组件化 ,我们来实现一个可以运行的demo,并讨论优缺点。

路由

MGJRouter 单例,通过 订阅或注册发布或使用 来实现。肯定有点模糊,我们开始代码化。

确定唯一标识

通过mgj://detail?id=:id 注册,通过 mgj://detail?id=5 传递参数,首先我们找到两个url的相同点,下面代码中 我通过一个简单的算法+ (NSString *)keyWithUrlStr:(NSString *)urlStr, 得到一个 唯一标识 mgj_detail_id*, 这样就可以通过map 来注册和使用

  • registerURLPattern:toHandler: 注册
  • openURL: 调用组件
@interface MGJRouter : NSObject
// 可以在 一个统一的地方来注释
//mgj://detail?id=:id
+ (void)registerURLPattern:(NSString *)pattern
                 toHandler:(void(^)(NSDictionary *dic))block;
// 去触发组件的回调
//mgj://detail?id=5
+ (void)openURL:(NSString *)urlStr;
@end

@interface MGJRouter()

@property (nonatomic,strong) NSMutableDictionary * map;

@end

@implementation MGJRouter

- (instancetype)init {
    if (self = [super init]) {
        self.map = [NSMutableDictionary dictionary];
    }
    return self;
}

+ (MGJRouter *)shareManager {
    static dispatch_once_t onceToken;
    static MGJRouter * router = nil;
    dispatch_once(&onceToken, ^{
        if (router == nil) {
            router = [[MGJRouter alloc] init];
        }
    });
    return router;
}

// 可以在 一个统一的地方来注释
+ (void)registerURLPattern:(NSString *)pattern
                 toHandler:(void(^)(NSDictionary *dic))block {
    
    NSString * key = [self keyWithUrlStr:pattern];
    [self shareManager].map[key] = block;
}

// 去触发组件的回调
+ (void)openURL:(NSString *)urlStr {
    
    NSString * key = [self keyWithUrlStr:urlStr];
    NSLog(@"key = %@",key);
    void(^block)(NSDictionary *dic) = [self shareManager].map[key];
    if (block) {
        NSMutableDictionary * param  = nil;
        NSURL * url = [NSURL URLWithString:urlStr];
        NSString * query = url.query;
        if (query.length) {
            NSArray * items = [query componentsSeparatedByString:@"&"];
            param = [NSMutableDictionary dictionary];
            for (NSString * item in items) {
                NSArray * littleItems = [item componentsSeparatedByString:@"="];
                NSString * itemFirst = littleItems.firstObject;
                NSString * itemSecond = littleItems.lastObject;
                if (itemFirst && itemSecond) {
                    param[itemFirst] = itemSecond;
                }
            }
        }
        block(param);
    }
}


//mgj://detail?id=:id
//协议名 mgj
// host detail
// query  id=:id
// 把 mgj://detail?id=:id 和 mgj://detail?id=5 转变成相同的唯一key
+ (NSString *)keyWithUrlStr:(NSString *)urlStr {
    NSURL * url = [NSURL URLWithString:urlStr];
    NSString * query = url.query;
    NSString * queryStr = @"";
    if (query.length) {
        NSArray * items = [query componentsSeparatedByString:@"&"];
        for (NSString * item in items) {
            NSString * itemFirst = [item componentsSeparatedByString:@"="].firstObject;
            queryStr = [queryStr stringByAppendingString:itemFirst];
            queryStr = [queryStr stringByAppendingString:@"*"];
        }
    }
    return [NSString stringWithFormat:@"%@_%@_%@",url.scheme,url.host,queryStr];
}

@end

测试组件

// 注册一个组件(一般在启动的时候去注册)
    [MGJRouter registerURLPattern:@"mgj://detail?id=:id&name=:name" toHandler:^(NSDictionary *dic) {
        NSString * oneId = dic[@"id"];
        NSString * name  = dic[@"name"];
        if (oneId && name) {
            //创建组件,并从字典拿到值
            DetailComposite * detail = [[DetailComposite alloc] init];
            detail.oneId = oneId;
            detail.name  = name;
            // 执行组件的方法
            [detail showComposite];
        }
    }];

    // 外界去调用 执行一个组件
    [MGJRouter openURL:@"mgj://detail?id=5&name=leeDev"];
    // 打印出: showComposite _ id = 5 ; name = leeDev

总结路由功能

其实就是使用 map 来存储 key -> 组件的功能 block ,通过 open 传递参数和key 直接调用这个block,并传递参数。

协议 (协议 - 类)

因为我们组件化,就是为了不暴露 我们的实现类,但是我们可以暴露一些接口,这样其实就是为了 降低耦合。

蘑菇街是通过 ModuleManager 来管理 协议 和 类的关联
主要是两个方法

@interface ModuleManager : NSObject
+ (void)registerClassName:(NSString *)className forProtocolName:(NSString *)protocolName;
+ (Class)classForProtocolName:(NSString *)protocolName;
@end



@interface ModuleManager()

@property (nonatomic,strong) NSMutableDictionary * map;

@end
@implementation ModuleManager


- (instancetype)init {
    if (self = [super init]) {
        self.map = [NSMutableDictionary dictionary];
    }
    return self;
}

+ (ModuleManager *)shareManager {
    static dispatch_once_t onceToken;
    static ModuleManager * router = nil;
    dispatch_once(&onceToken, ^{
        if (router == nil) {
            router = [[ModuleManager alloc] init];
        }
    });
    return router;
}

+ (void)registerClassName:(NSString *)className forProtocolName:(NSString *)protocolName {
    
    [self shareManager].map[protocolName] = className;
}

+ (Class)classForProtocolName:(NSString *)protocolName {
    
    NSString * className = [self shareManager].map[protocolName];
    return NSClassFromString(className);
}

@end

假设需要获取购物车的个数
我们定义一个协议

@protocol MGJCart <NSObject>
+ (NSInteger)orderCount;
@end

在正在实现类里面去实现这个协议

#import "MGjCart.h"
@interface MGJCartImp : NSObject<MGJCart>
@end


@implementation MGJCartImp
+ (NSInteger)orderCount {
    //处理逻辑,并返回结果
    return 40;
}
@end

注册和使用


//注册协议 我们只需要
    [ModuleManager registerClassName:@"MGJCartImp" forProtocolName:@"MGJCart"];
    
    //从class 获取的 ,就是我们 只是把 MGJCart 协议暴露出去
    Class cla = [ModuleManager classForProtocolName:@"MGJCart"];
    NSInteger orderCount = [(id <MGJCart>)cla orderCount];
    NSLog(@"orderCount = %@",@(orderCount));
    // 打印出 orderCount = 40

优缺点

优点

  • 降低耦合性,就是组件只是依赖url,而不需要依赖具体的类

缺点

  • 传递 image 等对象类型麻烦,url 不支持
  • 回调block 也很麻烦,是可以通过字典回调出来,但是需要文档写的比较清楚,才能回调出来
  • 协议 - 类的 使用,也是比较繁琐,一般是该类的实例是一个 单例对象,因为调用的都是 + 方法
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,530评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,403评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,120评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,770评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,758评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,649评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,021评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,675评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,931评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,751评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,410评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,004评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,969评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,042评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,493评论 2 343

推荐阅读更多精彩内容

  • 前言: 本文转自前同事casa的博文,这篇文章是基于runtime实现的iOS组件化方案,其实iOS组件化方案基本...
    monkey01阅读 1,650评论 1 2
  • 背景 随着公司业务的不断发展,项目的功能越来越复杂,各个业务代码耦合也越来越多,代码量也是急剧增加,传统的`MVC...
    China架构师阅读 821评论 0 4
  • 摘自原文:https://www.cnblogs.com/fishbay/p/7216084.html 最近研究了...
    三线小奋青阅读 515评论 0 1
  • 胡写。 心事重重,力不从心。这段时间事好多,难以宁静。 还是得调整一下自己。即便忙,每天也得尽可能给自己一会儿宁静...
    修禺心阅读 220评论 0 1
  • 在半空飘荡的不过是破塑料袋 伸在头顶的利爪不过是树影 跟在身后鬼鬼祟祟的不过是流浪狗 脚下咯吱发颤的不过是沙粒和石...
    无核苹果不伤芯阅读 209评论 0 0