耦合之殇,软件设计中的隐疾

来源:鉴源实验室
2025-04-03
4208

01

引 言

数据耦合和控制耦合是复杂软件设计中常见的一种情况。DO-178C要求高安全等级软件( DAL A/ B /C) 的测试覆盖分析,必须要确认基于需求的测试执行了代码组件之间的数据耦合和控制耦合。如果控制耦合未被正确分析,可能会导致系统在某些情况下做出错误的决策。因此当测试覆盖率未满足要求时,还要给出相关的解决方案。

02

定 义

从DO-178C中得到数据耦合与控制耦合的定义。

数据耦合:The dependence of a software component on data not exclusively under the control of that software component.某个软件组件对该软件组件控制下的非独占数据的依赖性。

控制耦合:The manner or degree by which one software component influences the execution of another software component. 某个软件组件对其它软件组件的执行所产生的影响方式或程度。

其中component的定义如下:

Component:A self contained part,combination of parts,subassemblies,or units that perform a distinct function of a system. 系统中能执行明确功能的独立部件、部件的组合、子组件或单元。

一般来说,Component可被解释为:过程、函数、子程序、模块和其他类似的编程构造。

具体目标在DO-178及其补充文件的目标矩阵表A-7中有详细的条目说明,完整表格参见附录A。

6.4.4.d:Test coverage of software structure, both data coupling and control coupling, is achieved. 软件结构测试覆盖的实现应该包含数据耦合和控制耦合。

在6.4.4.2.c中有着如下进一步的定义。

6.4.4.2.c:Analysis to confirm that the requirements-based testing has exercised the data and control coupling between code components. 分析以确认基于需求的测试已经实现了代码组件之间的数据和控制耦合。

DO-178B和DO-178C标准中的示例主要说明了:代码组件之间的数据和控制耦合的结构覆盖率分析应通过评估基于需求的测试结果来实现(见6.4.4.2.c)。但这些定义并没有清晰地确定要进行的实际检查。在一般情况下,它们针对的是其他覆盖技术无法检测到的故障。在接下来的讨论中,将详细探讨这些概念,以制定实现这些目标的实用技术。但是能够清晰得知的是,航空机载软件是需要针对数据耦合与控制耦合进行静态的分析,以及基于需求的测试覆盖。

03

实例解析

本章结合一些具体例子进行控制耦合与数据定义的详细说明。

3.1 控制耦合

例子1

以下具有三个文件的模型,每个文件中至少有一个对函数foo的调用。然而,其中两个文件包含函数foo的定义。这两个定义可以相同或可能相似(相同的接口)。

图1 文件及其定义示意图

链接器可以选择解析对文件A中的函数定义的所有调用(情况1),也可以对文件A中对其包含的定义的调用、文件B中对其包含的定义的引用和文件C中的调用中的任何一个进行解析(情况2)。这是一个控制耦合缺陷的例子,因为这导致了定义的模糊性。

例子2

第二个例子是参数的控制耦合。一个具有函数指针参数的函数foo,即可以通过foo的参数列表传递另一个函数的名称(地址)。

图2 例子2代码展示图

对于这段示例代码来说,通过下方两个用例可以实现代码动态执行的完全语句覆盖。

图3 例子2代码全覆盖用例图

可能有两个函数(func1与func2)在调用,而对于上述两个数据集,每个只调用一个函数,在用例1调用func1,在用例2调用func2。控制耦合要求在每个用例执行所有潜在的调用。为了确保执行所有控制流调用,基于需求的测试数据因此需要包括两个额外的数据集。这个概念可以应用于使用指向函数的指针的所有情况。如果通过指针解引用调用函数,则必须执行所有可以调用的潜在函数。同样,在C++等面向对象程序中,所有可以在特定调用的虚函数都必须要有基于需求的测试数据执行。

3.2 数据耦合

数据耦合完全取决于整个系统或子系统的控制流图结构。

例子1

如下图所示为官方文档给出的一个数据耦合的例子。

图4 数据耦合示例图

如上图所示Calculate Air Speed和Display Air Speed两个函数都是从同一个主程序调用的,并且共享一个全局变量“AirSpeed”。Calculate Air Speed计算变量“速度”的值,即设定操作,而功能Display Air Speed“速度”值输出到显示设备,即使用操作。代码可能采用下图所示的形式。

图5 数据耦合代码1展示图

可以构建一个执行Display Air Speed的测试用例,然后构建另一个执行计算空速的测试实例。在这种情况下,控制耦合按要求进行测试(即执行的每个语句和执行的每个分支/决策,在这种情况中没有MC/DC要求),但呼叫的顺序有缺陷,因为显示空速没有有效的显示速度。同样,如果一个测试用例调用Calculate Airspeed,而随后没有调用Display Airspeed,则很可能存在另一个缺陷。

这个例子表明,需要证明给定过程的所有输入在调用时都有有效值,并且使用了分配给全局变量的所有值。在这种情况下,全局变量可以看做在使用过程中外部声明和设置的变量。

例子2

如下所示,这个函数多次使用了变量x。

图6 数据耦合代码2展示图

主函数同样多次调用了foo函数。

图7 数据耦合代码3展示图

运行main函数,由于循环通过参数y调用foo可以让foo中的每条语句都被执行,此外还有每个分支/决策。然而使用参数z调用foo单个用例无法达到语句与分支/决策的全覆盖,因此需要多个测试用例来保证在z的值改变的情况下,foo函数的语句会被全部覆盖。根据上述的分析,主函数main与函数foo含有数据耦合,它们通过调用参数y与z,形参x而产生了耦合。 

04

耦合度启示

在这个纷繁复杂的世界中,耦合度分析的核心思想——系统思维、关注隐藏依赖、预防优于修复、优化资源分配以及透明性和可追溯性——不仅适用于软件开发和航空安全领域,还能为生产工作和生活带来深刻的启示。

1. 系统思维的重要性

耦合度分析强调从整体出发,理解各部分之间的相互关系。在生产中,这意味着要关注生产流程的全局,而不仅仅是单个环节。例如,工厂中每个车间的工作并非孤立存在,它们通过物料流转、信息传递等方式紧密相连。只有从系统层面优化这些耦合关系,才能真正提升效率。

2. 关注隐藏的依赖关系

耦合度分析提醒我们关注那些不明显但可能引发问题的隐藏依赖关系。在生产工作中,团队成员之间可能因职责划分不清而产生隐性依赖,例如某个部门的工作进度可能依赖于另一个部门的数据输出,但这种依赖并未明确说明。提前识别这些潜在问题,可以避免项目延误。

3. 预防优于修复

耦合度分析的核心是通过提前分析和测试,发现潜在问题,避免问题在后期放大。这种“预防优于修复”的理念在生产中体现为定期设备维护和流程优化,减少因设备故障或流程问题导致的生产中断。

4. 优化资源分配

耦合度分析可以帮助识别系统中的关键耦合点,从而优化资源分配,提高系统整体性能。在生产中,通过分析各部门之间的耦合关系,可以合理分配人力、物力资源,避免资源浪费。例如,优化供应链管理可以减少库存成本,同时提高响应速度。

5. 透明性和可追溯性

耦合度分析要求对系统中的耦合关系进行详细记录和分析,这种透明性和可追溯性有助于快速定位问题和优化系统。在生产中,通过记录生产流程中的各个环节及其耦合关系,可以快速追溯质量问题的根源,从而采取有效的改进措施。

05

耦合度应用

随着科技发展与技术创新,近年来轨交、汽车、航空航天等领域的发展非常迅猛,伴随而来的是各类安全问题。以上领域涉及的安全关键软件的质量与效率都受到了高度重视。因此,高可信嵌入式软件建模开发工具SmartRocket Modeler应运而生,诞生自质量,扎根于安全。通过模型语言的图形化建模、模型静态检查、仿真与调试与C代码生成等等功能为用户提供一套基于模型的高安全性嵌入式软件解决方案。在更进一步的前提下逐渐替代相关国外软件,为解决卡脖子难题做出贡献。

图8 SmartRocket Modeler主页图

在耦合度分析理论的基础上,上海控安SmartRocket Modeler团队,通过充分的调研与实践,实现了该功能的具体落地。

工程师根据上层需求,通过可视化建模方法得到其设计模型,其设计模型能够通过图形化或者代码字符串的方式进行展示,如下图所示为示例项目Roll Control的图形化界面。

图9 SmartRocket Modeler设计模型图形化展示界面

Modeler使用同步数据流语言Lustre代码进行设计模型的一致性描述,Lustre是一种经过严格形式化验证的设计模型语言,它能够充分描述机载软件在固定时钟下的运行情况,其示例 lustre 代码如下所示。

图10 RollControl飞机控制模块Lustre代码示意图

Modeler通过针对设计模型的分析给出其控制耦合与数据耦合的结果,并通过图形化与表格的多维度展示,为用户确认模型预期性提供便捷。

图11 SmartRocket Modeler耦合度分析图

Modeler通过耦合分析图来展示方法或函数间的控制的耦合,如图11所示。图中的点与边分别代表着函数与函数间的调用关系,正如第三章控制耦合的示例中从起点函数出发,存在多个调用的关系,也存在不同函数共同调用统一函数的关系。通过图的表现形式,生动展示了设计中的控制耦合关系。在其中,入度为零的点为全局变量和输入变量,出度为零的点为输出变量。该图可以便于用户快速定位搭建的模型与原始需求是否相一致。

图12 SmartRocket Modeler全局变量使用情况展示图

图12中的信息将函数调用关系以表格的形式进行了展示,该表中另外对实际调用过程中传递的参数进行映射,该表不仅展示了调用关系(控制耦合),更进一步包含了调用的参数及其类型(数据耦合),便于用户根据表格和需求进行比对。

图 13 SmartRocket Modeler全局变量使用情况展示图

此外Modeler还支持对全局变量的使用情况进行统计与展示,全局常量与传感器等变量在模块间进行着数据传输,如下图所示。此表目的用于方便用户映射需求的数据字典对应的全局变量使用情况是否符合用户预期。

Modeler 工具通过对系统中各个组件间的依赖关系进行深度分析,实现了对系统耦合度的全景式展示。具体而言,它会分析变量之间的相互依赖、函数调用链的深层次联系,以及函数对全局数据的依赖情况,并以直观的图形和层次结构展现出来。通过这种方式,设计者不仅可以清晰地看到哪些模块之间存在紧密耦合,还能识别出那些可能导致后期维护和扩展困难的高耦合区域。Modeler 工具的多层次、分组显示依赖关系,允许用户根据实际需求进行过滤和聚类,从而精准定位那些与需求设计偏离的部分。在模型设计的早期阶段,就能通过实时反馈机制发现潜在风险,及时调整设计策略,确保整个系统架构与业务需求保持高度一致。这样不仅提升了系统设计的效率,也为后续的系统优化和升级奠定了坚实基础。


参考文献

[1] Johnson L A. Software Considerations in Airborne Systems and Equipment Certification[Z]. Document RTCA/DO 178B,1998.

[2] RTCA.DO-248C-2011,Supporting Information for DO 178C and DO-278A[S]. USA: RTCA,2011.

[3] CAST.CAST-19-2004,Clarification of Structural Coverage Analyses of Data Coupling and Control Coupling[S]. USA: CAST,2004.

[4] 孔德岐,钟珊 .浅谈DO-178C中数据耦合与控制耦合目标的实现[J] .航空计算技术,2018,48(5):57-60.





阅读原文


收藏
点赞
2000