蘑菇街为了补全本地调用的功能,为组件多加了另一种方案,就是通过 protocol-class 注册表的方式。首先有一个新的中间件:
//ProtocolMediator.m 新中间件
@implementation ProtocolMediator
@property (nonatomic, storng) NSMutableDictionary *protocolCache
- (void)registerProtocol:(Protocol *)proto forClass:(Class)cls {
NSMutableDictionary *protocolCache;
[protocolCache setObject:cls forKey:NSStringFromProtocol(proto)];
}
- (Class)classForProtocol:(Protocol *)proto {
return protocolCache[NSStringFromProtocol(proto)];
}
@end
然后有一个公共Protocol文件,定义了每一个组件对外提供的接口:
//ComponentProtocol.h
@protocol BookDetailComponentProtocol <NSObject>
- (UIViewController *)bookDetailController:(NSString *)bookId;
- (UIImage *)coverImageWithBookId:(NSString *)bookId;
@end
@protocol ReviewComponentProtocol <NSObject>
- (UIViewController *)ReviewController:(NSString *)bookId;
@end
再在模块里实现这些接口,并在初始化时调用 registerProtocol 注册。
//BookDetailComponent 组件
#import "ProtocolMediator.h"
#import "ComponentProtocol.h"
#import "WRBookDetailViewController.h"
+ (void)initComponent
{
[[ProtocolMediator sharedInstance] registerProtocol:@protocol(BookDetailComponentProtocol) forClass:[self class];
}
- (UIViewController *)bookDetailController:(NSString *)bookId {
WRBookDetailViewController *detailVC = [[WRBookDetailViewController alloc] initWithBookId:param[@"bookId"]];
return detailVC;
}
- (UIImage *)coverImageWithBookId:(NSString *)bookId {
….
}
最后调用者通过 protocol 从 ProtocolMediator 拿到提供这些方法的 Class,再进行调用:
//WRReadingViewController.m 调用者
//ReadingViewController.m
#import "ProtocolMediator.h"
#import "ComponentProtocol.h"
+ (void)gotoDetail:(NSString *)bookId {
Class cls = [[ProtocolMediator sharedInstance] classForProtocol:BookDetailComponentProtocol];
id bookDetailComponent = [[cls alloc] init];
UIViewController *vc = [bookDetailComponent bookDetailController:bookId];
[[UIApplication sharedApplication].keyWindow.rootViewController.navigationController pushViewController:vc animated:YES];
}
这种思路有点绕,这个方案跟刚才两个最大的不同就是,它不是直接通过 Mediator 调用组件方法,而是通过 Mediator 拿到组件对象,再自行去调用组件方法。
结果就是组件方法的调用是分散在各地的,没有统一的入口,也就没法做组件不存在时的统一处理。组件1调用了组件2的方法,如果用前面两种方式,组件间是没有依赖的,组件1+Mediator可以单独抽离出来,只需要在Mediator里做好调用组件2方法时的异常处理就行。而这种方法组件1对组件2的调用分散在各个地方,没法做这些处理,在不修改组件1代码的情况下,组件1和组件2是分不开的。
当然你也可以在这上面跟方案1一样在 Mediator 对每一个组件接口 wrapper 一层,那这样这种方案跟方案1比除了更复杂点,其他没什么区别。
在 protocol-class 这个方案上,主要存在的问题就是分散调用导致耦合,另外实现上会有一些绕,其他就没什么了。casa 说的 “protocol对业务产生了侵入,且不符合黑盒模型。” 其实并没有这么夸张,实际上 protocol 对外提供组件方法,跟方案1在 Mediator wrapper 对外提供组件方法是差不多的。