基本概念
圈复杂度(Cyclomatic complexity,简写CC)也称为条件复杂度,是一种代码复杂度的衡量标准。由托马斯·J·麦凯布(Thomas J. McCabe, Sr.)于1976年提出,用来表示程序的复杂度,其符号为VG或是M。它可以用来衡量一个模块判定结构的复杂程度,数量上表现为独立现行路径条数,也可理解为覆盖所有的可能情况最少使用的测试用例数。圈复杂度大说明程序代码的判断逻辑复杂,可能质量低且难于测试和 维护。程序的可能错误和高的圈复杂度有着很大关系。
计算方法
1. 点边计算法
对于一个控制流图的圈复杂度,其计算公式为:
其中,为控制流图中边的数量,为节点数量。
举几个栗子:
顺序结构的圈复杂度为(无论多长的顺序结构):1
while循环圈复杂度(增加一个while循环圈复杂度+1):2
if-else圈复杂度(增加一个if分支圈复杂度+1):2
Until同while
2. 节点判别法
通过上述的点边计算法,可以发现,增加while分支或是if分支,圈复杂度就会+1,所以不难发现,每增加一个判定节点,圈复杂度就会+1,计算公式如下:
其中为判定节点数,主要有如下类型:
- if语句
- while语句
- for语句
- case语句
- catch语句
- and和or布尔操作(判定语句中)
- ?: 三元操作符
值得注意的是,对于多分支的case结构和if-elseif-else等结构,需要判断时间判定节点数,画个图就清楚了😊
计算栗子
栗子1
void sort(int * A)
{
int i=0;
int n=4;
int j = 0;
while(i < n-1)
{
j = i +1
while(j < n)
{
if (A[i] < A[j])
swap(A[i], A[j]);
}
i = i + 1
}
}
所以圈复杂度为:
当然也可以使用点边计算法计算
栗子2
U32 find (string match){
for(auto var : list)
{
if(var == match && from != INVALID_U32) return INVALID_U32;
}
//match step1
if(session == getName() && key == getKey())
{
for (auto& kv : Map)
{
if (kv.second == last && match == kv.first)
{
return last;
}
}
}
//match step2
auto var = Map.find(match);
if(var != Map.end()&& (from != var->second)) return var->second;
//match step3
for(auto var: Map)
{
if((var.first, match) && from != var.second)
{
return var.second;
}
}
return INVALID_U32;
};
圈复杂度:
意义
一般来说圈复杂度大于10的方法存在很大的出错风险。圈复杂度和缺陷个数有高度的正相关:圈复杂度最高的模块和方法,其缺陷个数也可能最多。
圈复杂度 | 代码状况 | 可测性 | 维护成本 |
1-10 | 清晰、结构化 | 高 | 低 |
10-20 | 复杂 | 中 | 中 |
20-30 | 非常复杂 | 低 | 高 |
>30 | 不可读 | 不可测 | 非常高 |
所以在平时的代码编写过程中要注意降低代码的圈复杂度,可以采用拆分函数和优化表达等方式降低圈复杂度😘