iOS开发-APP组件模块化的理解

模块化的意义

当项目大到一定程度,开发人员也多,所有的代码都集中到一个仓库,提交修改都要等其他人提交完成不报错才能开始,非常麻烦。

再者就是代码之间耦合严重,到处引用,穿插错综复杂,往往改变一个变量,需要修改很多处代码,很容易出错。

对于这种情况,就要进行架构整治,模块化无疑是一个好的方案。


模块化的粒度

对于模块化,并不是一味的全部分离成模块就是最好的。模块之间或许会有必要的引用以及上下级依赖关系,没必要完全的独立。再者需要考虑到业务变化时,可能又要重新划分模块,工作量和成本又高了。

iOS 模块化的划分应该遵循 SOLID原则 ,如下几点:

  • (S)单一原则:对象的功能要单一,不要是很多功能的集合体

例如CALayer负责动画视图显示,UIView则负责事件传递,事件响应

  • (O)开闭原则:“开”,是指对于组件功能的扩展是开放的,是允许对其进行功能扩展的;开闭原则中“闭”,是指对于原有代码的修改是封闭的,即不应该修改原有的代码。
  • (L)里氏替换原则:子类对象是可以替换基类对象的。所有引用基类的地方必须能透明地使用其子类的对象。

例如KVO的实现机制,利用isa-swizzling父类指向子类。

  • (I)接口隔离原则:接口的用途要单一,不要一个接口上根据入参不同实现不同的功能。

可以使用多个专门的协议,而不是使用一个庞大臃肿的协议。

  • (D)依赖反转原则:方法应该依赖抽象,不要依赖实例。面向接口编程,不要面向实现编程。让调用接口感觉不到内部是如何操作。

还一个 (D)迪米特法则:如果两个软件实体无须直接通信,那么就不应当发生直接的相互调用,可以通过第三方转发该调用。其目的是降低类之间的耦合度,提高模块的相对独立性。即高聚合,低耦合。
可以顺带记忆


组件

可组装,独立的业务单元,高内聚,低耦合的特性。

iOS开发的组件,不应是UI控件,也不是UIViewController这种大UI和功能的集合,应该是包含UI控件,小功能集合的组件这样划分。


组件化方案

MGJRouter 的路由映射

https://github.com/meili/MGJRouter

蘑菇街的组件化方案 MGJRouter

[MGJRouter registerURLPattern:@"mgj://detail?id=:id" toHandler:^(NSDictionary *routerParameters) {
    NSNumber *id = routerParameters[@"id"];
    // create view controller with id
    // push view controller
}];

是一个路由方案url-block,通过注册urlblock到一个单例中的字典routes中。
当调用

[MGJRouter openURL:@"mgj://detail?id=404"]

就通过Url去从字典routes取其block来执行。这样内存中维护了很多映射关系,Url这样的使用方式,也比较繁琐。而且需要将Url的时机必须要在调用之前,在+load进行注册又拖长了App启动时间,在其他时机注册又需要统筹兼顾。

另外还有protocol-classurl-controller,这些方式都大同小异,需要维护一个映射表,是一个短板,个人更倾向于CTMediator这种中间者架构


CTMediator 中间者

https://github.com/casatwy/CTMediator

其实就是使用runtime通过[target performSelector:action withObject:params];取到需要的数据,例如界面,视图。

如何使用这些界面视图就不关CTMediator的事了,这些界面,视图就可以封装成单独的模块,互无关系。


Category的编写

CTMediator的关键在于其分类的编写,每个模块功能的分类由其模块开发者编写。

- (UIViewController *)CTMediator_viewControllerForDetail
{
    UIViewController *viewController = [self performTarget:kCTMediatorTargetA
                                                    action:kCTMediatorActionNativeFetchDetailViewController
                                                    params:@{@"key":@"value"}
                                         shouldCacheTarget:NO
                                        ];
    if ([viewController isKindOfClass:[UIViewController class]]) {
        // view controller 交付出去之后,可以由外界选择是push还是present
        return viewController;
    } else {
        // 这里处理异常场景,具体如何处理取决于产品
        return [[UIViewController alloc] init];
    }
}

这是demo中分类的一个方法实现,kCTMediatorTargetA隶属于模块A

kCTMediatorTargetA中有一个kCTMediatorActionNativeFetchDetailViewController方法可以获取一个UIViewController`,

通过写CTMediator对于模块A的分类,就可以获取模块A中相关的界面以及UI,例如CTMediator_viewControllerForDetail

在这里插入图片描述
这样的优势在于,没有了key-value的映射关系,不需要维护url映射表,更不需要存储block块,Category For A由开发模块A的人员来写,他很熟悉模块A(对于他来说将模块A中某个界面取出,并设置相关参数是很简单的事情),通过分类再转给不熟悉的人(无需关心模块A的内容)使用,就很方便了。

对于远程url调用,同样是通过将url解析成对应的动作,例如取某个视图,推出某个界面。


组件化能让工程架构更加清晰,每个人负责一个模块,通过pod导入,互不影响。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章