蘑菇街为了补全本地调用的功能,为组件多加了另一种方案,就是通过 protocol-class 注册表的方式。首先有一个新的中间件:

    1. //ProtocolMediator.m 新中间件
    2. @implementation ProtocolMediator
    3. @property (nonatomic, storng) NSMutableDictionary *protocolCache
    4. - (void)registerProtocol:(Protocol *)proto forClass:(Class)cls {
    5. NSMutableDictionary *protocolCache;
    6. [protocolCache setObject:cls forKey:NSStringFromProtocol(proto)];
    7. }
    8. - (Class)classForProtocol:(Protocol *)proto {
    9. return protocolCache[NSStringFromProtocol(proto)];
    10. }
    11. @end

    然后有一个公共Protocol文件,定义了每一个组件对外提供的接口:

    1. //ComponentProtocol.h
    2. @protocol BookDetailComponentProtocol <NSObject>
    3. - (UIViewController *)bookDetailController:(NSString *)bookId;
    4. - (UIImage *)coverImageWithBookId:(NSString *)bookId;
    5. @end
    6. @protocol ReviewComponentProtocol <NSObject>
    7. - (UIViewController *)ReviewController:(NSString *)bookId;
    8. @end

    再在模块里实现这些接口,并在初始化时调用 registerProtocol 注册。

    1. //BookDetailComponent 组件
    2. #import "ProtocolMediator.h"
    3. #import "ComponentProtocol.h"
    4. #import "WRBookDetailViewController.h"
    5. + (void)initComponent
    6. {
    7. [[ProtocolMediator sharedInstance] registerProtocol:@protocol(BookDetailComponentProtocol) forClass:[self class];
    8. }
    9. - (UIViewController *)bookDetailController:(NSString *)bookId {
    10. WRBookDetailViewController *detailVC = [[WRBookDetailViewController alloc] initWithBookId:param[@"bookId"]];
    11. return detailVC;
    12. }
    13. - (UIImage *)coverImageWithBookId:(NSString *)bookId {
    14. ….
    15. }

    最后调用者通过 protocol 从 ProtocolMediator 拿到提供这些方法的 Class,再进行调用:

    1. //WRReadingViewController.m 调用者
    2. //ReadingViewController.m
    3. #import "ProtocolMediator.h"
    4. #import "ComponentProtocol.h"
    5. + (void)gotoDetail:(NSString *)bookId {
    6. Class cls = [[ProtocolMediator sharedInstance] classForProtocol:BookDetailComponentProtocol];
    7. id bookDetailComponent = [[cls alloc] init];
    8. UIViewController *vc = [bookDetailComponent bookDetailController:bookId];
    9. [[UIApplication sharedApplication].keyWindow.rootViewController.navigationController pushViewController:vc animated:YES];
    10. }

    这种思路有点绕,这个方案跟刚才两个最大的不同就是,它不是直接通过 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 对外提供组件方法是差不多的。