为了研究组件化,我们主要是讨论
蘑菇街的路由+协议式
和中间件
讨论第一种方式,并参考 蘑菇街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 也很麻烦,是可以通过字典回调出来,但是需要文档写的比较清楚,才能回调出来
- 协议 - 类的 使用,也是比较繁琐,一般是该类的实例是一个
单例对象
,因为调用的都是+ 方法
。