iOS 组件化之CTMediator

关于iOS组件化网上资料太多,这里只是从个人观点说明一下怎么使用组件化和使用组件化的优点和缺点
首先下载CTMediatorDemo

Demo的目录结构


截屏2020-07-09 09.49.14.png

项目在没有使用CTMediator之前模块间的关系是这样的


截屏2020-07-09 09.52.37.png

当ModuleA要调用ModuleB和ModuleC的时候,需要#import ModuleB,ModuleC
同样ModuleB要调用ModuleA和ModuleC的时候,需要#import ModuleA,ModuleC 这样耦合程度非常严重

使用了CTMediator之后


截屏2020-07-09 10.01.10.png

ModuleA,ModuleB,ModuleC之间就没有耦合了。

具体实现
CTMediator+ModuleA

#import "CTMediator.h"


NS_ASSUME_NONNULL_BEGIN

@interface CTMediator (ModuleA)

- (BaseViewController *)mediator_ModuleAPage1ViewController:(NSMutableDictionary *)params;

- (BaseViewController *)mediator_ModuleAPage2ViewController:(NSMutableDictionary *)params completion:(void (^)(NSDictionary *))completion;

@end

NS_ASSUME_NONNULL_END
#import "CTMediator+ModuleA.h"

//  字符串 是类名 Target_xxx.h 中的 xxx 部分
NSString * const kCTMediatorTarget_ModuleA = @"ModuleA";
//  字符串是 Target_xxx.h 中 定义的 Action_xxxx 函数名的 xxx 部分
NSString * const kCTMediatorActionNativTo_ModuleAPage1ViewController = @"ModuleAPage1ViewController";
NSString * const kCTMediatorActionNativTo_ModuleAPage2ViewController = @"ModuleAPage2ViewController";

@implementation CTMediator (ModuleA)

- (BaseViewController *)mediator_ModuleAPage1ViewController:(NSMutableDictionary *)params{
   BaseViewController *viewController = [self performTarget:kCTMediatorTarget_ModuleA
                                                   action:kCTMediatorActionNativTo_ModuleAPage1ViewController
                                                   params:params shouldCacheTarget:NO];
   if ([viewController isKindOfClass:[UIViewController class]]) {
       // view controller 交付出去之后,可以由外界选择是push还是present
       return viewController;
   } else {
       // 这里处理异常场景,具体如何处理取决于产品
       NSLog(@"%@ 未能实例化页面", NSStringFromSelector(_cmd));
       return [[BaseViewController alloc] init];
   }
}

- (BaseViewController *)mediator_ModuleAPage2ViewController:(NSMutableDictionary *)params completion:(void (^)(NSDictionary *))completion{
    [params setValue:completion forKey:@"callback"];
   BaseViewController *viewController = [self performTarget:kCTMediatorTarget_ModuleA
                                                     action:kCTMediatorActionNativTo_ModuleAPage2ViewController
                                                     params:params shouldCacheTarget:NO];
     if ([viewController isKindOfClass:[UIViewController class]]) {
         // view controller 交付出去之后,可以由外界选择是push还是present
         return viewController;
     } else {
         // 这里处理异常场景,具体如何处理取决于产品
         NSLog(@"%@ 未能实例化页面", NSStringFromSelector(_cmd));
         return [[BaseViewController alloc] init];
     }
}

@end

Target_ModuleA

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface Target_ModuleA : NSObject

- (BaseViewController *)Action_ModuleAPage1ViewController:(NSDictionary *)params;

- (BaseViewController *)Action_ModuleAPage2ViewController:(NSDictionary *)params;

@end

NS_ASSUME_NONNULL_END
#import "Target_ModuleA.h"
#import "ModuleAPage1ViewController.h"
#import "ModuleAPage2ViewController.h"

 typedef void (^CallbackType)(NSDictionary *);

@implementation Target_ModuleA

- (BaseViewController *)Action_ModuleAPage1ViewController:(NSDictionary *)params{
    ModuleAPage1ViewController *vc = [[ModuleAPage1ViewController alloc]init];
    vc.parameter = params;
    return vc;
}

- (BaseViewController *)Action_ModuleAPage2ViewController:(NSDictionary *)params{
   
    CallbackType callback = params[@"callback"];
    if (callback) {
        callback(@{@"status":@"success"});
    }
    ModuleAPage2ViewController *vc = [[ModuleAPage2ViewController alloc]init];
    vc.parameter = params;
    return vc;
}


@end

ModuleAPage1ViewController

#import "ModuleAPage1ViewController.h"

@interface ModuleAPage1ViewController ()<UITableViewDelegate,UITableViewDataSource>

@property(strong, nonatomic) UITableView *tableView;
@property(strong, nonatomic) NSArray *dataArray;

@end

@implementation ModuleAPage1ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    
    self.view.backgroundColor = [UIColor whiteColor];
    [self setupTableView];
    self.dataArray = @[@"ModuleAPage2",@"ModuleBPage1",@"ModuleCPage1"];
    [self.tableView reloadData];
}

- (void)setupTableView{
    self.tableView = [[UITableView alloc]initWithFrame:self.view.bounds style:UITableViewStylePlain];
    self.tableView.delegate = self;
    self.tableView.dataSource = self;
    [self.view addSubview:self.tableView];
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
    return self.dataArray.count;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *cellIndetifier = @"cell";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIndetifier];
    if (cell == nil) {
        cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIndetifier];
    }
    cell.textLabel.text = self.dataArray[indexPath.row];
    return cell;;
}

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
    return 44.f;
}

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
    switch (indexPath.row) {
        case 0:
            [self pushModuleAPage2];
            break;
        case 1:
            [self pushModuleBPage1];
            break;
        case 2:
            [self presentModuleCPage1];
            break;
            
        default:
            break;
    }
    
}

- (void)pushModuleAPage2
{
    NSMutableDictionary *param = [NSMutableDictionary dictionary];
    [param setValue:@"ModuleAPage1" forKey:@"page"];
    UIViewController *viewController = [[CTMediator sharedInstance] mediator_ModuleAPage2ViewController:param completion:^(NSDictionary * _Nonnull dic) {
        NSLog(@"%@",dic);
        
    }];
     [viewController setHidesBottomBarWhenPushed:YES];
    [self.navigationController pushViewController:viewController animated:YES];
}

- (void)pushModuleBPage1
{
     UIViewController *viewController = [[CTMediator sharedInstance] mediator_ModuleBPage1ViewController];
     [viewController setHidesBottomBarWhenPushed:YES];
    [self.navigationController pushViewController:viewController animated:YES];
}

- (void)presentModuleCPage1
{
     UIViewController *viewController = [[CTMediator sharedInstance] mediator_ModuleCPage1ViewController];
    [self.navigationController presentViewController:viewController animated:NO completion:nil];
}

/*
#pragma mark - Navigation

// In a storyboard-based application, you will often want to do a little preparation before navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
    // Get the new view controller using [segue destinationViewController].
    // Pass the selected object to the new view controller.
}
*/

@end

只粘贴一部分代码,其余可以看Demo,

从ModuleAPage1ViewController中代码可看出跳转部分都没有#import Module
这就是我们说的说的组件化。传统方式是我们需要跳转那个页面,就需要#import相关的页面,但是使用了CTMediator之后我们只要知道这个页面是属于那个Module,或者说属于那个组件,然后直接调用相关的组件即可。

组件化的优点

举个例子,公司某个App有一个登录???,过一段时间需要研发一个新的App,为了节省时间就用之前App有的登录???,就需要把登录??槌槿〕隼醋龀勺榧?,可能某些同学就会问,这不是私有化Pod库就能实现吗?

接着说即使把登录??樗接谢疨od后,那么假如在B项目中ModuleA,ModuleB,ModuleC都需要验证,如果没有登录就调用登录模块,哪又回到了之前耦合的问题上了ModuleA,ModuleB,ModuleC都需要#import 登录???,所以私有化只是组件的一部分。只有实现组件化才能解耦。

怎么判断项目需要组件化

1.需要组件化首先就需要模块化,就是对业务的高度抽象。需要把相关的业务都抽取到一个??槔锩?。这就不适合创业公司或者只有一个开发人员的公司。小公司和创业公司基本都是在试错,业务基本不稳定。高度抽象业务很难。如果在创业公司项目因业务不稳定,也不建议使用组件化。

2.公司如果只有一个开发人员也不建议使用组件化,组件化最大难度就是高度抽取业务,抽取出来组件化也需要维护,像我们公司一个人开发维护2个App哪就不要给自己找罪受了。

3.如果公司有2个以上开发人员并且时间相对充裕的情况下,在公司业务相对稳定,的情况下可以考虑使用组件化。

4.公司有2个及以上App,有重合的业务可以优先考虑先把重合业务抽取成组件。

以上仅代表个人观点,具体项目中需不需组件化还是要看自己,自己想怎么用就怎么用。
最后编辑于
?著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,128评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,316评论 3 388
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事?!?“怎么了?”我有些...
    开封第一讲书人阅读 159,737评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,283评论 1 287
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,384评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,458评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,467评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,251评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,688评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,980评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,155评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,818评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,492评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,142评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,382评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,020评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,044评论 2 352