插件式模块化软件框架的思想图解一(框架篇)
- Chapter1 插件式模块化软件框架的思想图解一(框架篇)
- 一、前述
- 二、模块化原则
- 1、高度独立
- 2、接口规范
- 三、从管理需求出发
- 四、框架雏形
- 五、接口引用规定
- 六、子模块与代码模板
- 七、把优秀当作一种习惯
- 八、信息交换方式
- 九、模块编号和ID的唯一性
- 十、框架总图
- 十一、插件拔插(一键删除不留残余)
- Chapter2 插件式模块化软件框架的思想图解二(案例篇)
- 一、前言
- 二、框架总图
- 三、模块划分
- 四、配置及资源文件目录结构设计
- 五、对象的个性化派生设计
- 六、源码文件目录结构设计
- 七、数据交换机设计
- 八、最终效果图
- 九、后话
Chapter1 插件式模块化软件框架的思想图解一(框架篇)
原文链接:https://blog.csdn.net/guestcode/article/details/119701789
一、前述
模块化开发具有开发效率高、开发周期短等特点(其它优点不再赘述)。从本人20多年多个行业众多项目经历来说,无论是一个人开发、两个人开发,还是多人协同开发,模块化在实际应用中有非常高的实效性。
本文讨论的是开发期单体进程内源代码插件式框架思想。如果没有特别说明,本文提及的“插件”和”模块”是同一个实体概念、部分章节提及的“单元”和“子模块”是同一实体概念。
本文循序渐进讨论插件式框架思想,框架具体实现细节会因项目不同而有差别,本文不做具体阐述。
二、模块化原则
1、高度独立
要达到高度独立必须实行高度解耦。不仅要求模块代码、模块数据类型、配置文件部分高度独立,还要求模块代码文件高度独立在模块目录内,即模块专有的各类信息、源代码和代码文件均独立存在模块目录内部,不能分散在其它模块或框架内部。
2、接口规范
模块化设计就强调框架接口、模块接口的规范化。不管是C++还是其他语言,接口部分应该独立为一个单元文件,接口单元只保留接口相关的信息,并确保避免交叉引用。如果A模块使用B模块的接口,但B模块又引用到C模块的数据类型,那么B模块应该把C模块的类型做别名处理,保证A模块一次引用单元文件即可完成对B模块的使用操作。如果接口单元间出现交叉引用,那可能是模块功能划分不够合理。禁止跳过接口单元直接引用模块内部单元。
三、从管理需求出发
主导一个框架原形的不是技术基础,而是需求管理,因为在业务需求多变的情况下,如何从容快速应对变化是管理的根本问题。实际项目中,无论在开发初期,还是维护期,或者是后期的版本迭代,往往变的是业务模块,管理上就提出需求:仅变更或删除需求变动的部分。根据业务模块需求可分为:基本模块、功能模块(本文暂定名称)。基础模块是几乎不变动的业务逻辑部分,功能模块是最善变的业务需求部分。
四、框架雏形
框架分为:基础框架、业务框架。基础框架是通用的,和项目业务逻辑没有关系的。业务框架是和业务逻辑相关的,为业务模块提供基础的。
五、接口引用规定
在强调解耦高度独立的模块化系统中,它们的引用关系(依赖关系)就必须有硬性要求:“框架层”不能引用“业务模块层”(含基础模块和功能模块)、“业务基础模块层”不能引用“业务功能模块层”、“业务功能模块层”之间可以互相引用。
六、子模块与代码模板
无论单个业务模块的规模大小,每个业务模块可以根据功能划分为若干个子模块(单元文件)。因为我们都知道,不管单元文件还是函数方法,当它的代码行数超过一定数量的时候,共用的函数和变量就会随之增多,高度耦合的代码是不便于维护的。子模块的接口无需独立成单独文件,仅需考虑代码的私有和公共部分即可。当子模块形成一个既有定式的时候,我们就可以使用模板工具生成结构化的模块代码,那么模块开发工作就更加便捷了。
备注: 接口单元可能定义了公共的数据类型,所以接口单元有可能会被多个子模块引用。
七、把优秀当作一种习惯
说到模块化和子模块化的时候,随之而来的是增加接口代码的工作量问题,虽然代码量不多但还是增加了麻烦。先不说管理规范制度问题了,就说习惯问题:当习惯养成了以后,一切都是自然而然的事情。
八、信息交换方式
框架向模块推送消息:模块需向框架订阅消息,框架向模块推送消息(消息参数不能完全传递信息的情况下,模块可以向框架获取消息详细内容)。模块与模块的信息交换是通过数据交换机完成,可以避免模块单元相互引用,并且达到解耦要求(每个模块都有独立的数据存储单元,交换机仅是事件驱动和信息传递无存储功能)。当然并非100%的模块能走数据交换通道,按82法则,即使有20%的模块需要直接引用模块接口单元,在模块化要求下它们直接的依赖关系已经大大减少。
九、模块编号和ID的唯一性
对于独立模块内部定义的各类ID,又如何保证它具有全局唯一性?模块编号(模块ID)可以解决这个问题。模块编号是全局统筹具有全局唯一性的数值,基于模块编号乘以某个基数或者以字节为单位来移位,我们就可以在模块内部独立生成不同类型具有全局唯一性的ID。对外公开的ID在模块接口单元定义,模块内部使用的ID在模块类型定义单元定义。框架和模块接口对唯一性ID的注册均进行合法性检查。
十、框架总图
我们需要对需求进行共性和个性分析,以得出清晰的脉络关系。基础模块层从业务框架层派生了基础模块层的对象和接口(下图中⑴处),各个基础模块直接引用本层派生的接口。功能模块层从基础模块层派生了功能模块层的对象和接口(下图中⑵处),各个功能模块直接引用本层派生的接口。另外功能模块的子模块还可以从本层再派生功能对象(下图中⑶处),实现了极强个性化的需求。
十一、插件拔插(一键删除不留残余)
插件拔除能确保模块无残余: 当版本迭代的时候,一键删除“模块代码文件总目录”和“模块配置文件总目录”即可;单独拔除某个模块的时候,一键删除该模块的“代码文件目录”和“配置文件目录”即可(有引用关系的则再解除引用部分即可)。插件插入时把模块目录加入到项目目录中即可。插件式模块化框架大大提高了开发效率和缩短了开发周期。
姐妹篇《插件式模块化软件框架的思想图解二(案例篇)》:
Chapter2 插件式模块化软件框架的思想图解二(案例篇)
原文链接:https://blog.csdn.net/guestcode/article/details/119981524
一、前言
本人推崇模块化设计,不是基于技术深度,而是基于管理高度(如何在多变的项目需求中提高开发效率、缩短开发周期)。本文将通过一个《火车在途实时信息系统》的火车实时和历史轨迹显示部分的简要阐述来“插件式模块化软件框架思想”的理解和应用(实际项目设计要复杂得多)。
本文涉及项目截图均已向社会公众公开,本文仅阐述行业惯例或通用做法部分,不涉及商业秘密和专利核心技术。
阅读本文前最好先阅读本人上一篇博文《插件式模块化软件框架的思想图解一(框架篇)》:
插件式模块化软件框架的思想图解一(框架篇)(码客卢益贵)_ygluu的博客-CSDN博客
二、框架总图
为保证文章完整性,借用《框架篇》的框架总图。
三、模块划分
按业务需求的功能大类划分,功能模块划分好了,再去设计基础模块如何为功能模块提供服务,再深入设计“业务框架”。我把项目划分为“实时监控模块(MonitorRealTime)”和“历史数据查询模块(QueryHistory)”等若干模块(命名是为了排序需要,无关英文语法),仅举例两个模块就不作图了。
实时监控和历史轨迹都要显示火车头和轨迹信息,具体业务需求是:实时监控时火车轨迹图标是红色,历史查询时轨迹图标是蓝色,两个模块的火车头图标颜色相同。
四、配置及资源文件目录结构设计
配置及资源文件有:电子地图图层文件、素材图标文件、参数配置文件等。电子地图资源的加载需要依赖一个叫“GisMap.cfg”的配置文件,加载一个资源文件需要配置一行相关参数。原Gis系统的要求是统一配置在一个多行文件里,我做了特别处理:按模块需求分多个文件配置。
基础图层的配置信息放在公共目录处:/ResCfgs/Comm/TrainGisMap/GisMap.cfg。火车头图标的配置信息放在基础模块目录处:/ResCfgs/MuduleBase/TrainGisMap/TrainHead.cfg。红色和蓝色轨迹图标的配置信息放在功能模块目录处:/ResCfgs/MuduleFunc/MonitorRealTime/TraceRed.cfg、/ResCfgs/MuduleFunc/QueryHistory/TraceBlue.cfg。
这样,资源配置部分也满足了模块高度独立的原则要求(关于模块化原则和业务模块共性与个性划分请参阅《框架篇》),确保了模块配置文件目录删除不留残余(这在版本迭代时不会导致代码残余积累,也不会因为误删导致系统崩溃)。
五、对象的个性化派生设计
根据模块需求,对对象进行了个性化划分,作为最上层(图形最底层)的功能模块对象,即使这个模块删除了不会影响该对象父类被其他模块使用,也不会因为删除而留有残余在父类。
六、源码文件目录结构设计
目录设计思想(模块目录独立)可参考《框架篇》的模块化原则和目录总图。
七、数据交换机设计
类似消息中心有订阅、通知功能,但和消息中心不同,消息中心是简报形式,数据交换机是大数量的信息交换中心。
我们处理的数据有GPS轨迹数据和火车黑匣子数据,每项数据的都用唯一字符串Key来定义,向数据交换机注册数据类型的时候,数据交换机会转换成整形值,避免了高频情况下字符串处理的低效率问题。
在这个机制下,GPS和黑匣子这两个数据处理模块的代码任何一个或者全部移除,都不会导致系统崩溃,也就是当数据消费模块(持久化和监控模块)主动向数据交换机获取数据的时候返回结果是0而已。
在中后期的持续调研中发现,火车安装的黑匣子有多个厂家的且同一个厂家还有不同型号,于是我们基于原有的模块接口标准,在不改变原来的任何代码情况下,针对不同型号的黑匣子做了N个型号的黑匣子数据处理模块(插件),系统在运行时自动识别黑匣子型号并自动插入相应的数据处理模块(插件),并且可以进行热拔插(切换)不影响系统运行。
八、最终效果图
实时监控图(红色轨迹)
历史数据查询图(蓝色轨迹)
九、后话
插件模块化不一定适用所有项目或者项目中的所有功能,但按82法则,满足80%的业务需求就足够了。先不说后期维护,在多人协同开发时,制定接口规范之后个人就可以独立自主开发,在开发期能避免更多的沟通成本。