為了研究組件化,我們主要是討論
蘑菇街的路由+協(xié)議式
和中間件
討論第一種方式涕侈,并參考 蘑菇街IOS組件化 羹奉,我們來實(shí)現(xiàn)一個可以運(yùn)行的demo秒旋,并討論優(yōu)缺點(diǎn)。
路由
用
MGJRouter
單例尘奏,通過訂閱或注冊
和發(fā)布或使用
來實(shí)現(xiàn)滩褥。肯定有點(diǎn)模糊炫加,我們開始代碼化。
確定唯一標(biāo)識
通過mgj://detail?id=:id
注冊铺然,通過 mgj://detail?id=5
傳遞參數(shù)俗孝,首先我們找到兩個url的相同點(diǎn),下面代碼中 我通過一個簡單的算法+ (NSString *)keyWithUrlStr:(NSString *)urlStr
, 得到一個 唯一標(biāo)識 mgj_detail_id*
, 這樣就可以通過map 來注冊和使用
-
registerURLPattern:toHandler:
注冊 -
openURL:
調(diào)用組件
@interface MGJRouter : NSObject
// 可以在 一個統(tǒng)一的地方來注釋
//mgj://detail?id=:id
+ (void)registerURLPattern:(NSString *)pattern
toHandler:(void(^)(NSDictionary *dic))block;
// 去觸發(fā)組件的回調(diào)
//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;
}
// 可以在 一個統(tǒng)一的地方來注釋
+ (void)registerURLPattern:(NSString *)pattern
toHandler:(void(^)(NSDictionary *dic))block {
NSString * key = [self keyWithUrlStr:pattern];
[self shareManager].map[key] = block;
}
// 去觸發(fā)組件的回調(diào)
+ (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
//協(xié)議名 mgj
// host detail
// query id=:id
// 把 mgj://detail?id=:id 和 mgj://detail?id=5 轉(zhuǎn)變成相同的唯一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) {
//創(chuàng)建組件魄健,并從字典拿到值
DetailComposite * detail = [[DetailComposite alloc] init];
detail.oneId = oneId;
detail.name = name;
// 執(zhí)行組件的方法
[detail showComposite];
}
}];
// 外界去調(diào)用 執(zhí)行一個組件
[MGJRouter openURL:@"mgj://detail?id=5&name=leeDev"];
// 打印出: showComposite _ id = 5 ; name = leeDev
總結(jié)路由功能
其實(shí)就是使用 map 來存儲 key
-> 組件的功能 block
,通過 open 傳遞參數(shù)和key
直接調(diào)用這個block赋铝,并傳遞參數(shù)。
協(xié)議 (協(xié)議 - 類)
因?yàn)槲覀兘M件化沽瘦,就是為了不暴露 我們的實(shí)現(xiàn)類革骨,但是我們可以暴露一些接口,這樣其實(shí)就是為了 降低耦合析恋。
蘑菇街是通過 ModuleManager
來管理 協(xié)議 和 類的關(guān)聯(lián)
主要是兩個方法
@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
假設(shè)需要獲取購物車的個數(shù)
我們定義一個協(xié)議
@protocol MGJCart <NSObject>
+ (NSInteger)orderCount;
@end
在正在實(shí)現(xiàn)類里面去實(shí)現(xiàn)這個協(xié)議
#import "MGjCart.h"
@interface MGJCartImp : NSObject<MGJCart>
@end
@implementation MGJCartImp
+ (NSInteger)orderCount {
//處理邏輯良哲,并返回結(jié)果
return 40;
}
@end
注冊和使用
//注冊協(xié)議 我們只需要
[ModuleManager registerClassName:@"MGJCartImp" forProtocolName:@"MGJCart"];
//從class 獲取的 ,就是我們 只是把 MGJCart 協(xié)議暴露出去
Class cla = [ModuleManager classForProtocolName:@"MGJCart"];
NSInteger orderCount = [(id <MGJCart>)cla orderCount];
NSLog(@"orderCount = %@",@(orderCount));
// 打印出 orderCount = 40
優(yōu)缺點(diǎn)
優(yōu)點(diǎn)
- 降低耦合性助隧,就是組件只是依賴url筑凫,而不需要依賴具體的類
缺點(diǎn)
- 傳遞 image 等對象類型麻煩,url 不支持
- 回調(diào)block 也很麻煩,是可以通過字典回調(diào)出來巍实,但是需要文檔寫的比較清楚滓技,才能回調(diào)出來
- 協(xié)議 - 類的 使用,也是比較繁瑣棚潦,一般是該類的實(shí)例是一個
單例對象
令漂,因?yàn)檎{(diào)用的都是+ 方法
。