什么是代码圈复杂度?
圈复杂度(Cyclomatic Complexity)是一种代码复杂度的衡量标准,由 Thomas McCabe 于 1976年定义。它可以用来衡量一个模块判定结构的复杂程度,数量上表现为独立现行路径条数,也可理解为覆盖所有的可能情况最少使用的测试用例数。圈复杂度大说明程序代码的判断逻辑复杂,可能质量低且难于测试和维护。程序的可能错误和高的圈复杂度有着很大关系。
圈复杂度主要与分支语句(if、else、,switch 等)的个数成正相关。可以在图1中看到常用到的几种语句的控制流图(表示程序执行流程的有向图)。当一段代码中含有较多的分支语句,其逻辑复杂程度就会增加。在计算圈复杂度时,可以通过程序控制流图方便的计算出来。
代码圈复杂度的计算方法
通常使用的计算公式是V(G) = e – n + 2 , e 代表在控制流图中的边的数量(对应代码中顺序结构的部分),n 代表在控制流图中的节点数量,包括起点和终点(1、所有终点只计算一次,即便有多个return或者throw;2、节点对应代码中的分支语句)。
增加圈复杂度的语句:在代码中的表现形式:在一段代码中含有很多的 if / else 语句或者其他的判定语句(if / else , switch / case , for , while , | | , ? , …)。
代码示例-控制流图
根据公式 V(G) = e – n + 2 = 12 – 8 + 2 = 6 ,上图的圈复杂段为6。
说明一下为什么n = 8,虽然图上的真正节点有12个,但是其中有5个节点为throw、return,这样的节点为end节点,只能记做一个。
采用圈复杂度去衡量代码的好处
1.指出极复杂模块或方法,这样的模块或方法也许可以进一步细化。
2.限制程序逻辑过长。
McCabe&Associates 公司建议尽可能使 V(G) <= 10。NIST(国家标准技术研究所)认为在一些特定情形下,模块圈复杂度上限放宽到 15 会比较合适。
因此圈复杂度 V(G)与代码质量的关系如下:
V(G) ∈ [ 0 , 10 ]:代码质量不错;
V(G) ∈ [ 11 , 15 ]:可能存在需要拆分的代码,应当尽可能想措施重构;
V(G) ∈ [ 16 , ∞ ):必须进行重构;
3.方便做测试计划,确定测试重点。
许多研究指出一模块及方法的圈复杂度和其中的缺陷个数有相关性,许多这类研究发现圈复杂度和模块或者方法的缺陷个数有正相关的关系:圈复杂度最高的模块及方法,其中的缺陷个数也最多,做测试时做重点测试。
可以降低圈复杂度的重构技术
1.Extract Method(提炼函数)
代码段可被组织在一起并独立出来
void printOwing(double previousAmount)
{
Enumeration e = _orders.elements();
double outstanding = previousAmount * 1.2;
// print banner
System.out.println ("* * * * * * * * * * * * * * * * * * * * * * *");
System.out.println ("Customer Owes ");
System.out.println (" * * * * * * * * * * * * * * * * * * * * * * *");
// calculate outstanding
while (e.hasMoreElements())
{
Order each = (Order) e.nextElement();
outstanding += each.getAmount();
}
//print details
System.out.println ("name:" +_name);
System.out.println ("amount" + outstanding)
}