循环复杂度

本页使用了标题或全文手工转换
维基百科,自由的百科全书

循环复杂度Cyclomatic complexity)也称为条件复杂度圈复杂度,是一种软体度量,是由老托马斯·J·麦凯布英语Thomas J. McCabe, Sr.在1976年提出,用来表示程式的复杂度,其符号为VG或是M。循环复杂度由程式的源代码中量测线性独立路径的个数。此概念有些类似的量测文字复杂度的Flesch-Kincaid可读性测试英语Flesch-Kincaid Readability Test ,不过方法不完全相同。

循环复杂度是由程式的控制流图来计算:有向图的节点对应程式中个别的程式码,而若一个程式执行后会立刻执行另一程式码,会有边连结二程式码对应的节点。圈复杂度可应用在程式的子程序模组方法类别

麦凯布首先提出一种称为“基础路径测试”(Basis Path Testing)的软体测试方式,是测试程式中的每一线性独立路径,此情形的测试用例个数即为程式的循环复杂度[1]

“循环复杂度”的名称有时会让人误解,因为此复杂度不只计算程式中的回圈(循环)个数。循环复杂度是指程式的控制流图中,若将结束点到启始点再增加一个边时,控制流图中的圈(几个边形成封闭路径)的个数[2]

原理[编辑]

简单程式的控制流图。此程式由红色的节点开始执行,然后进入回圈(红色节点下由三个节点组成),离开回圈后有条件分支,最后执行蓝色节点后结束,此控制流图中,E = 9, N = 8, P = 1,因此其循环复杂度为 9 - 8 + (2*1) = 3

一段程式的循环复杂度是其线性独立路径的数量。若程式中没有像IF指令或FOR回圈的控制流程,因为程式中只有一个路径,其循环复杂度为1,若程式中有一个IF指令,会有二个不同路径,分别对应IF条件成立及不成立的情形,因此循环复杂度为2。

数学上,一个结构化程式[note 1]的循环复杂度是利用程式的控制流图来定义,控制流图是一个有向图,图中的节点为程式的基础模块,若一个模块结束后,可能会执行另一个模块,则用箭头连结二个模块,并标示可能的执行顺序。循环复杂度M可以用下式定义[2]

M = EN + 2P

其中

E 为图中边的个数
N 为图中节点的个数
P 为图中连通分量的个数
对应同一个程式的控制流图,但多加一个从结束点到启始点的边,因此为强连通的控制流图,若利用此图计算循环复杂度,其公式为M=E-N+P,而E = 10、N = 8、P = 1,因此循环复杂度为3

另一个计算循环复杂度的公式,需修改控制流图,每一个结束点都增加一个到启始点的边。修改后的图称为强连通,任何二个节点A和B,都可以找到从A到B及从B到A的路径。程式的循环复杂度等于此图中回路的个数(也称为第一贝蒂数),其公式如下[2]

M = EN + P

上式可以视为计算图中线性独立回路(回路内不包括其他回路)的个数,由于控制流图增加结束点到启始点的边,因此对应一个结束点至少会有一个回路。

对于单一的程式(或副程式或方法),P恒为1。但循环复杂度可以适用于同时分析许多程式或副程式的情形(例如针对一个类别中的所有方法),此时P等于程式的个数,因为每一个程式的图都是一个独立的连接元件。若每一个程式都只一个结束点,P也可以视为是结束点的个数。

可以证明任何只有一个进入点及结束点的结构化程式,其循环复杂度等于程式中决策点(if指令及条件回圈)个数加1[2][3]

循环复杂度也可以延伸到多个结束点的程式,此时的循环复杂度如下:

π - s + 2

其中

π是程式中决策点的个数
s为结束点的个数[3][4]

应用[编辑]

限制软体复杂度[编辑]

麦凯布提出循环复杂度时,其原始目的之一就是希望在软体开发过程中就限制其复杂度。他建议程式设计者需计算其开发模组的复杂度,若一模组的循环复杂度超过10,需再分割为更小的模组[2]。NIST(国家标准技术研究所)的结构化测试方法论已此作法略作调整,在一些特定情形下,模组循环复杂度上限放宽到15会比较合适。此方法论也承认有些特殊情形下,模组的复杂度需要超过上述的上限,其建议为“模组的循环复杂度需在上限范围以内,否则需提供书面资料,说明为何此模组循环复杂度有必要超过上限。”[5]

模组内聚性的评估[编辑]

可以预期一个复杂度较高模组的内聚性会比较低,至少不会到功能内聚性的程度。一个有高复杂度及低内聚性的模组中会有许多的决策点,这类的模组多半执行超过一个明确定义的任务,因此内聚性较低。一个2005年的研究发现复杂度的度量和由专家评估的模组内聚性有高度负相关,反而针对内聚性设计的度量和专家评估结果之间的相关性还比较不明显[6]

推测软体缺陷个数[编辑]

许多研究指出一模组及方法的循环复杂度和其中的缺陷个数有相关性,许多这类研究发现循环复杂度和缺陷个数有高度的正相关:循环复杂度最高的模组及方法,其中的缺陷个数也最多。

不过,有些研究是在控制模组大小相近的情形下进行分析(例如比较二个源代码行数相近,但循环复杂度不同模组的缺陷个数),许多这类的研究发现循环复杂度和缺陷个数没有明显相关,不过仍有一些研究认为在此情形下二者仍有相关性。有些此领域的研究者认为那些研究结果循环复杂度和缺陷个数没有明显相关的研究,其研究方法的有效性可能有问题[7]

莱斯·哈顿英语Les Hatton认为利用循环复杂度来预测缺陷个数,和利用源代码行数来预测缺陷个数的结果大致相近[8]

相关条目[编辑]

注解[编辑]

  1. ^ 此处的结构化特别强调一个函式只有单一的结束点

参考资料[编辑]

  1. ^ A J Sojev. Basis Path Testing. [2012-09-30]. (原始内容存档于2012-04-26). 
  2. ^ 2.0 2.1 2.2 2.3 2.4 McCabe. A Complexity Measure (PDF). IEEE Transactions on Software Engineering. December 1976: 308–320 [2012-10-01]. (原始内容 (PDF)存档于2022-05-30). 
  3. ^ 3.0 3.1 Belzer, Kent, Holzman and Williams. Encyclopedia of Computer Science and Technology. CRC Press. 1992: 367–368. 
  4. ^ Harrison. Applying Mccabe's complexity measure to multiple-exit programs. Software: Practice and Experience (J Wiley & Sons). October 1984. 
  5. ^ McCabe, Watson. Structured Testing: A Testing Methodology Using the Cyclomatic Complexity Metric. 1996. (原始内容存档于2012-08-28). 
  6. ^ Stein; Cox, Glenn; Etzkorn, Letha; et al. Exploring the relationship between cohesion and complexity. Journal of Computer Science. 2005, 1 (2): 137–144 [2012-10-02]. doi:10.3844/jcssp.2005.137.144. (原始内容存档于2019-12-02). 
  7. ^ Kan. Metrics and Models in Software Quality Engineering. Addison-Wesley. 2003: 316–317. ISBN 0-201-72915-6. 
  8. ^ Les Hatton. The role of empiricism in improving the reliability of future software. 2008. version 1.1. (原始内容存档于2012-11-03).