组件
组件(Component)是基于可重用为目的,将一个大的软件系统按照分离关注点的形式,拆分成多个独立的组件, 组件的出现是为了解决全局工程中有很多重复代码的问题,是为了复用,而且划分力度是相对较小的模块。 组件化的另一个目的是为了解耦,把系统拆分成多个组件,分离组件边界和责任,便于独立升级和维护。所以组件是内聚的,与前端的组件类似(前端的组件有自己的UI与逻辑),使用时直接被Import即可。
所以对于服务端的组件,我们希望也是可以直接被Import的,导入即可用。例如我们常说工具类,在某种意义上也是粒度很小的组件。常见的组件有技术层面的工具组件,例如把多个工具类封装来完成相对复杂的功能并对外提供API。 还有业务组件,为了完成某种业务功能,业务组件可能有自己的数据配置数据存储,业务数据(可能基于数据库表或其他存储介质),并通过服务的形式来给外部使用。
组件关键词:复用,业务逻辑内聚(同一个组件的代码至少应该在同一个Class或Package里,不应该在不同的系统里,不同的模块里,不同的包里)
插件
另一个容易混淆的概念是插件。插件可以理解为封装了一层对外调用的接口的组件,插件也是一种组件,不过有一定的约束,对外有统一的交互入口,例如有相同的接口, 所以可以简单理解插件化就是接口化。
插件的概念比较形象,一般存在一个“插拔”过程,所以要求可插拔的插件有一个相同的接口 (这里所说的接口只是概念上的接口,即调用方法及参数等)。而组件是不存在这个相同接口的,因为组件各不相同。
拿我们最常见的网络请求功能举例,无论哪种开发语言,可能都有多种网络请求组件,那么对于一个项目而言, 从一个网络组件如HttpClient切换为另一个网络组件OKHttp是基本无法做到调用方法不改动的。 而如果把网络请求组件插件化,即在组件外层抽象一层统一化的调用接口NetworkInterface, 然后将当前使用网络请求组件HttpClient包装成实现该接口的网络请求插件PluginA。 那么如果以后需要将使用的HttpClient切换为OkHttp,就只需要将OkHttp包装成PluginB并插入到应用中即可。 实际调用时,业务代码还是调用NetworkInterface,不用做任何修改。 从上面这个例子我们可以看出,插件和组件的实质区别就在于通过统一接口隔绝业务代码对于组件的直接依赖, 这也是我们常听到的所谓的“项目开发时应该把第三方组件封装一下再用”,或者叫防腐层设计。
模块
模块它是以关注点进行划分的,而关注点说到底就是功能或业务。
模块化的目的在于将一个程序按照其功能做拆分,分成相互独立的模块,以便于每个模块只包含与其功能相关的内容,模块之间通过接口调用。将一个大的系统模块化之后,每个模块都可以被高度复用。但是值得注意的是模块不等于功能,二者的关系大概为:功能 > 模块。一个功能可能包含多个模块。
模块也需要是高内聚,将分属同一模块代码放到一起;降低耦合,将不同模块间的耦合程度弱化。
总结
- 组件:代码重用,功能相对单一或者独立,无统一接口。组件化开发的成果是基础库和公共组件。
- 插件:近乎组件,有统一接口,可以说是封装了一层对外调用的接口的组件。
- 模块:高内聚,松耦合,功能相对复杂,有多个统一接口。模块化开发的基础是框架。
首先,可以肯定的是,组件化和模块化的中心思想都是分而治之。目的都是将一个庞大的系统拆分成多个组件或者说是模块。
而且如果大的组件也可以称为模块,小的模块也可以称为组件,所以,在我看来组件和模块的划分并没有那么的泾渭分明。随意两者的粒度的大小改变,两者是可以转换的。
最后说一下,这三个概念是经常同时出现在一个项目中的,我们往往对复杂大项目进行模块化划分的时候,也会进行组件化,而且插件化的本质是面向接口编程,对于组件化和模块化都是适用的,可实现随意插拔的灵活和高扩展性,属于项目架构的高端设计。