首页/开发/代码质量与评审速查/圈复杂度(Cyclomatic Complexity)
技术债与度量63 条

圈复杂度(Cyclomatic Complexity)

McCabe 1976 年提出:以控制流图判定点计数衡量分支规模,公式 M = E − N + 2P(边 − 节点 + 2×连通分量),实践中约等于判定点数量 + 1;它是分支测试数量的下界参考。

圈复杂度是什么

圈复杂度由 Thomas J. McCabe 在 1976 年提出:以控制流图上的判定点计数衡量分支规模,公式 M = E − N + 2P,也就是边数减节点数再加两倍连通分量,实践中约等于判定点个数加一。它同时给出分支测试数量的下界参考——复杂度为 4 的函数,至少要 4 条路径级用例才算覆盖到位。

基本信息

提出Thomas J. McCabe,1976 年论文 A Complexity Measure
计算公式M = E − N + 2P,等价于 1 加判定点个数
常见阈值约 10,属于经验值而非标准
主要用途可测试性评估与模块拆分参考

典型使用场景

  • 函数大到难以测试:先用复杂度找出最该拆的几个
  • 评审判断风险:高复杂度叠加高频变更,通常是缺陷高发区
  • 制定检查阈值:先看现状分布,再定多数函数能达到的目标值
  • 排查历史缺陷集中区:热点分析中复杂度是常用的一维
  • 评估测试充分性:复杂度给出分支用例数量的下界参考

哪些结构会被计入

  • if 与 else if 分支
  • for、while、do-while 循环
  • switch 的 case 分支
  • 部分工具把逻辑与或的短路求值也计入
  • catch 块与三元表达式

计数示例

计数示例
int f(int a, int b) {
    if (a > 0) { ... }        // +1
    while (b-- > 0) { ... }   // +1
    return a > b ? 1 : 0;     // +1
}
// M = 1 + 3 = 4,至少需要 4 条路径级的用例

取值区间与常见处理

复杂度含义常见处理
1 到 10结构简单,容易测试无需特别处理
11 到 20分支较多,测试成本上升评审时确认是否有必要拆分
20 以上难以完整测试优先拆分为多个函数
复杂度要配合变更频率一起看:高频改动且复杂度高的文件,才是优先重构的对象。
常见阈值属于经验值:它用于触发讨论与拆分判断,不适合直接拿来做个人考核。
不同工具的计算口径不一样:逻辑运算符是否计入、分支如何统计都会影响结果,跨工具比较之前先对齐规则。

易错点

  • 把复杂度当绩效指标:把代码拆碎,但逻辑本身的复杂并没有减少
  • 忽略工具口径差异:跨工具比较得出互相矛盾的结论
  • 只看复杂度不看变更频率:常年不动的复杂代码其实优先级很低
  • 为降低数字而拆分函数:参数传递与状态共享变多,理解成本反而上升
  • 只看复杂度不看测试覆盖:高风险分支即便被拆开,仍然没有用例验证

数据来源与参考

  • Thomas J. McCabe(1976)《A Complexity Measure》(IEEE TSE)
​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌