近几年,组件化和插件化在Android开发领域中一直是较为热门的议题,笔者也参与了组件化框架项目:JIMU,原DDComponentForAndroid,对组件化也算偶有心得。
本文不去做JIMU的具体介绍,也不将其和Andromeda(iqiyi开源组件化项目)、微信的API化轻量级方案等进行对比,仅结合这半年多来在JIMU讨论组中和各位同行的一些交流、一些issue的讨论,总体反思一下组件化。
其实张明庆老哥在JIMU的系列文档中零碎的提到过不少内容,之所以还有这一篇,是笔者觉得:在诸位同行在进行组件化实施探索的过程中出现的一些问题、困惑,还是对组件化理解的不够透彻导致的
模块化 & 组件化 或 插件化
请注意本处副标题中的三个名字,我们常常将组件化和插件化技术进行对比,这是一组概念,但是我们往往忽略掉了“模块化”,其实模块化才是组件化或插件化的前提。
简述一下计算机领域中广义的模块化含义:
以功能块为单位进行程序设计,实现其求解算法的方法称为模块化
而从方法论上看,模块化的核心要义就是要做到:构建功能相对独立的子系统,并通过子系统组合成一个实际产品。
而组件化的定义:(今天Wikipedia上不去了,援引百度百科)
组件化是指解耦复杂系统时将多个功能模块拆分、重组的过程,有多种属性、状态反映其内部特性。
可以说,组件化是进行一种系统解耦时、具有特定形式的模块化过程。
DEMO:在狭义讨论中
-
在软件设计中,我们“整理”出一个局部子系统,其内部实现了:
- 下层:使用线程池管理数据库CURD、文件存储等数据持久化的耗时任务,并暴露任务的管理
- 中层:DAO接口及其实现类的注册、通过DAO接口发现实现类的IOC容器(实现类对象的生命周期管理此处忽略讨论)
- 上层:DAO接口定义和DAO接口实现
这里我们可以将下层和中层划归为一个模块:比如叫“持久层操作核心模块”,将整个可以称为:“数据持久化模块”;在项目的整体分层划分中,我们可能更多的去用“数据持久化模块”去描述这个子系统,但其中的上层其实是做具体业务实现了,而“持久层操作核心模块”的讨论意义在这里更大一些。
假如、我提出一个比较极端的骚操作:将整个项目中所有的持久化业务集中在一起(前文中的“上层”),包括“持久层操作核心模块”,放在一个具体的Module中,将其称为:“持久化业务组件”,显然、这并没有什么错,但是这个做法很可能造成灾难。
但是将“持久层操作核心模块”称为:“持久层操作核心组件”的话,大家一定不会接受这个说法,它还没有上升到可以被称为组件的程度。打个比方,组件仅到分子的程度、模块可以到原子的程度。
我们先快速的抛出一个结论:要做好组件化一定要做好模块化,包含具体业务并通过接口向外暴露业务的模块可以被称为组件。
-
粒度问题,真实案例:考虑到改造时的风险性,某位朋友(就叫Tom好了)在组件化实施过程中并没有做前文中的“持久化业务组件”,而是将其散落在各个“实际功能系统组件”中。例如,Tom定义并实现了“用户系统组件”,其中包含了用户登录注册等等等等你下意识想到的业务,其中包含了用户设置的持久化业务,而在另一个组件中,需要使用到这个设置,最开始我们就“JIMU中组件初始化顺序可能影响到业务”进行讨论、最终讨论到组件化的粒度问题。
在1中,我们所看到的“持久化业务组件”就是一个粒度很细的例子,在Tom的案例中,就是一个粒度较粗的例子,粒度的粗细问题是没有一个定论的,只能根据实际情况选择一个平衡点,在Tom提供的案例中,可以在用户组件中定义业务接口,将业务暴露出去供其他组件使用,这并没有违反组件化原则,但是当这个“用户组件”越来越大时,将诸如:查询用户设置、查询用户信息等业务再拆分出一个独立的业务组件是有必要,这也是重构的一个适应症。
组件化革命不一定要一步到位、干净彻底,但一定要朝着正确的方向稳步发展。
在前面两个DEMO中我们对组件化和模块化有了一个感性的认识,我们再正儿八经的看概念
概念和方法论
模块化编程:
采用模块式开发方式,单个组件包括模板,数据结构,程序,样式四部份。
组件的接口表达了由该组件提供的功能和调用它时所需要的参数。
组件是可以单独开发、测试。允许多人同时协作,编写及开发、研究不同的功能模块。--百度词条
基于组件的软件工程(Component-based software engineering,简称CBSE)或基于组件的开发(Component-Based Development,简称CBD)是一种软件开发范型。它是现今软件复用理论实用化的研究热点,在组件对象模型的支持下,通过复用已有的构件,软件开发者可以“即插即用”地快速构造应用软件。这样不仅可以节省时间和经费,提高工作效率,而且可以产生更加规范、更加可靠的应用软件。-- wikipedia
百度词条中的概念,在Android中进行印证,组件包含“布局文件”(模板),数据类(广义的数据结构),程序,资源、样式。
JIMU sample工程中的ComponentService-Module,其中定义了组件提供的功能和入参出参牵涉到的数据类(基础类型除外)。
在wikipedia的概念中,我们抓住几个关键词:“组件对象模型”、“复用”、“即插即用”。
组件对象模型最早应该是在Windows开发中兴起的(未经考证),Component Object Model,他是用来描述一个组件的,不过不是用一个实际的实现类对象来描述的,而是通过接口规范来描述的,对照JIMU,其组件对象模型是APPLike生命周期接口和ComponentService中的业务API接口。
组件级复用有这样的要求:具有相对独立的业务环境,若对外界环境有所依赖,应该通过组件对象模型。通俗的说就是要解耦。
在复用已有的构件(构成组件,而非构建)上,JIMU做的也非常出色,他不仅能对源码级支持(这是必要的),同时对已经构建过的构件同样能做到复用。
“即插即用”:Plug-And-Play 本身是硬件层的一个词,在这里大体可以理解为:“修改配置或者输入命令、将构件纳入应用软件中,它就能直接使用而不需要过多的人工干涉”。在JIMU中,需要人维护配置表,做一点做的还不够好,但是在运行期间的热插拔方面做的还可以。
再回到百度词条,组件为何是可以单独开发、测试的。上文我们提到了组件对象模型、并且提到了组件解耦的要求;对于外部而言,组件就是一个对象模型,访问它只需要通过接口,而该组件对外部环境的依赖也是通过对象模型。所以、组件是完全独立的,他的实现和外界是无关的,即所谓独立开发。组件要运行是需要运行时环境的,只要满足了运行时环境、组件就可以运行以及测试,而组件对其他组件的依赖是通过组件对象模型的,意味着可以Mock,或者自己写Dummy实现(搞事情),进而独立测试逻辑(无论是单元测试、还是人工、自动化集成测试)。
从方法论上来说,组件化实现需要做这些:
- 一个组件化构建工具。在必要的时候满足:组件构建时具有运行时环境;在构建时的人工干预越小越好,最好能通过命令参数“即插即用”。
- 项目按照模块化进行设计、组件按照组件对象模型标准设计。
JIMU中关于独立测试、构建的具体操作请参考WIKI相关内容
直接使用JIMU
这张图是我之前画的,按照图示任务主线可以比较从容的完成项目改造,从容未必简单,仅仅是目的明确所带来的从容。即使不使用JIMU(或者原来的DDComponentForAndroid)以下内容也是必要的:
- 各种资源的整理,公共基础资源独立,私有资源跟随组件。某些资源可能在两三个Module中出现,但是作为公共资源觉得膈应的也可以改名后采取冗余。
- 公共库和私有库的区分。
- 梳理组件、但是先进行模块化。对于不符合模块化要求的代码,先做模块化改造,至少现在还没有实际的代码边界,修修改改代码还能跑
- 摘选合适的模块分离为组件,已经满足模块化的代码应该比较轻松了
- 处理组件化代码隔离带来的一些问题。这些问题可能并不存在,例如模块化过程中对于UI跳转也进行了彻底的解耦,那么组件化隔离时多半不会受到代码边界的影响
- 不停的拆分完善,并为单独组件补充高质量的测试,可以在迭代过程中减少不必要的错误
切忌
组件化开发一定要做到组件独立开发,可以多个组件同时独立开发,但一定要独立开发,这个独立不是指单人力,而是指组件的开发过程中,不要依赖还在开发中的组件对象模型。
组件对象模型的定义优先于组件对象的实现,诸如:组件对象模型的定义改了而使用者不知道的担心必然是冗余的。
写在最后
且不和其他项目做仔细的比较,JIMU在组件化课题中还有很多不足的地方,笔者也在为JIMU做一些力所能及的工作,欢迎各位对项目点个Star、发现并提出Issue、提交有价值的PR;也欢迎进群进行交流和讨论。
JIMU的讨论群,(QQ)群号693097923,欢迎大家加入: