在公司项目开发中,如果项目比较小,普通的单工程+MVC架构就可以满足大多数需求了。但是像淘宝、蘑菇街,滴滴这样的大型项目,原有的单工程架构就不足以满足架构需求了。
原因
在一个项目越来越大时,开发人员越来越多时,项目会出现很多问题
- 业务模块间划分不清晰,模块之间耦合度很大,非常难维护。
- 所有模块代码都编写在一个项目中,测试某个模块或功能,需要编译运行整个项目。
组件化优势
所以组件化的使用应运而生。所谓组件化就是把每一个业务划分成一个模块(组件),并且建立一个主项目,这个主项目负责集成所有组件。这样带来的好处是很多的:
- 业务划分更佳清晰,新人接手更佳容易,可以按组件分配开发任务。
- 项目可维护性更强,提高开发效率。
- 更好排查问题,某个组件出现问题,直接对组件进行处理。
- 开发测试过程中,可以只编译自己那部分代码,不需要编译整个项目代码。
- 方便集成,项目需要哪个模块直接通过CocoaPods集成即可。
组件化架构图及实现方案
组件化架构图
从上面的架构图可以清晰的看出:组件化架构中,首先有一个主工程,主工程负责集成所有组件。每个组件都是一个单独的工程,创建不同的git私有仓库来管理,每个组件都有对应的开发人员负责开发。开发人员只需要关注与其相关组件的代码,其他业务代码和其无关,来新人也好上手。
组件的划分需要注意组件粒度,粒度根据业务可大可小。对于一些多个组件共同的东西,例如网络、数据库之类的,应该划分到单独的组件或基础组件中。对于图片或配置表这样的资源文件,应该再单独划分一个资源组件,这样避免资源的重复性。
服务方组件对外提供服务,由中间件调用或发现服务,服务对当前组件无侵入性,只负责对传递过来的数据进行解析和组件内调用的功能。需要被其他组件调用的组件都是服务方,服务方也可以调用其他组件的服务。
通过这样的组件划分,组件的开发进度不会受其他业务的影响,可以多个组件单独的并行开发。组件间的通信都交给中间件来进行,需要通信的类只需要接触中间件,而中间件不需要耦合其他组件,这就实现了组件间的解耦。中间件负责处理所有组件之间的调度,在所有组件之间起到控制核心的作用。
这套框架清晰的划分了不同组件,从整体架构上来约束开发人员进行组件化开发,避免某个开发人员偷懒直接引用头文件,产生组件间的耦合,破坏整体架构。假设以后某个业务发生大的改变,需要对相关代码进行重构,可以在单个组件进行重构。组件化架构降低了重构的风险,保证了代码的健壮性。
组件集成
每个组件都是一个单独的工程,在组件开发完成后上传到git仓库。主工程通过Cocoapods集成各个组件,集成和更新组件时只需要pod update即可。这样就是把每个组件当做第三方来管理,管理起来非常方便。
Cocoapods可以控制每个组件的版本,例如在主项目中回滚某个组件到特定版本,就可以通过修改podfile文件实现。选择Cocoapods主要因为其本身功能很强大,可以很方便的集成整个项目,也有利于代码的复用。通过这种集成方式,可以很好的避免在传统项目中代码冲突的问题。
在主项目中集成组件主要分为两种方式:源码和framework,但都是通过CocoaPods来集成。无论是用CocoaPods管理源码,还是直接管理framework,效果都是一样的,都是可以直接进行pod update之类的操作的
这两种组件集成方案,实践中也是各有利弊。直接在主工程中集成代码文件, 可以在主工程中进行调试。集成framework的方式, 可以加快编译速度,而且 对每个组件的代码有很好的保密性。如果公司对代码安全比较看重,可以考虑framework的形式,但framework不利于主工程中的调试。
组件化使用
组件化的实践过程中,核心问题是各个模块的拆分,解耦,以及相互之间的通信。下面以蘑菇街为例进行解读。
耦合严重的工程
为了解决上面的问题,可以考虑加一个中间层来协调模块间的调用,所有的模块间的调用都会经过中间层中转。(注意看两张图的箭头方向)
但是发现增加这个中间层后,耦合还是存在的。中间层对被调用模块存在耦合,其他模块也需要耦合中间层才能发起调用。这样还是存在之前的相互耦合的问题,而且本质上比之前更麻烦了。
组件化架构
注意上面的架构模式,每个组件都与中间层进行交互,但是并不是可逆的,这也就做到了分层解耦。进行组件化开发后,可以把每个组件当做一个独立的app,每个组件甚至可以采取不同的架构,例如分别使用MVVM
、MVC
、MVCS
等架构。
MGJRouter方案
蘑菇街通过MGJRouter
实现中间层,通过MGJRouter
进行组件间的消息转发,从名字上来说更像是“路由器”。实现方式大致是,在提供服务的组件中提前注册block
,然后在调用方组件中通过URL
调用block
,下面是调用方式。
MGJRouter组件化架构
MGJRouter是一个单例对象,在其内部维护着一个“URL -> block”格式的注册表,通过这个注册表来保存服务方注册的block,以及使调用方可以通过URL映射出block,并通过MGJRouter对服务方发起调用。
在服务方组件中都对外提供一个接口类,在接口类内部实现block的注册工作,以及block对外提供服务的代码实现。每一个block都对应着一个URL,调用方可以通过URL对block发起调用。
在程序开始运行时,需要将所有服务方的接口类实例化,以完成这个注册工作,使MGJRouter中所有服务方的block可以正常提供服务。在这个服务注册完成后,就可以被调用方调起并提供服务。
关于组件化开发的理论知识就介绍到这里,关于具体实现,后续文章会有非常详细的介绍。
如有错误,欢迎指正!