圈复杂度的概念
圈复杂度(Cyclomatic complexity,CC)也称为条件复杂度,是一种衡量代码复杂度的标准,其符号为V(G)。
麦凯布最早提出一种称为“基础路径测试”(Basis Path Testing)的软件测试方式,测试程序中的每一线性独立路径,所需的测试用例个数即为程序的圈复杂度。
圈复杂度可以用来衡量一个模块判定结构的复杂程度,其数量上表现为独立路径的条数,也可理解为覆盖所有的可能情况最少使用的测试用例个数。<br />圈复杂度可应用在程序的子程序、模块、方法或类别。
圈复杂度与出错风险
程序的可能错误和高的圈复杂度有着很大关系,圈复杂度最高的模块和方法,其缺陷个数也可能最多。<br />圈复杂度大说明程序代码的判断逻辑复杂,可能质量低,且难于测试和维护。
代码复杂度低,代码不一定好,但代码复杂度高,代码一定不好。
圈复杂度 | 代码状况 | 可测性 | 维护成本 |
---|---|---|---|
1 - 10 | 清晰,结构化 | 高 | 低 |
10 -20 | 复杂 | 中 | 中 |
20 - 30 | 非常复杂 | 低 | 高 |
>30 | 不可读 | 不可测 | 非常高 |
一般来说,圈复杂度大于10的方法存在很大的出错风险。
圈复杂度与测试
测试驱动的开发 与 较低圈复杂度值 之间存在着紧密联系。<br />因为在编写测试用例时,开发人员会首先考虑代码的可测试性,从而倾向编写简单的代码(因为复杂的代码难以测试)。<br />一个好的测试用例设计经验是:创建数量与被测代码圈复杂度值相等的测试用例,以此提升测试用例对代码的分支覆盖率。
计算方法
通常有两者方式可以计算 V(G),不管用哪种方式计算,结果都是一样的。
点边计算法
V(G) = e - n + 2
其中 e 为控制流程图中边的数量,n 表示控制流图中节点的数量。
分割面积法
V(G) = R
其中 R 表示被控制流程图分割的面积的数量。
控制流程图
示例
示例一
function a() {
// codes
}
示例二
function a(x) {
if (x === 1) {
// codes
} else if (x === 2) {
// codes
} else {
// codes
}
}
示例三
function a(x) {
for (let i = 0; i < x.length; i++) {
if(x[i]) {
// codes
} else {
// codes
}
}
}
使用 ESLint 限制代码的圈复杂度
只需要配置 ESLint 的 complexity 属性即可,数组中的第一个元素为提示等级,第二个元素为最大圈复杂度的阈值(默认为 20)。
plugins: ["@typescript-eslint", "unused-imports", "rulesdir"],
rules: {
"no-debugger": 1,
+ complexity: ["error", 10],
"no-undefined": 1,