圈复杂度、认知复杂度

尝试提高代码质量,可以尝试考虑对代码的复杂性进行度量。常见的度量指标包括圈复杂度(Cyclomatic Complexity)和认知复杂度(Cognitive Complexity),它们分别关注代码的逻辑复杂性和开发者理解代码的难度。

一、圈复杂度(Cyclomatic Complexity) #

1.1 定义与计算 #

圈复杂度由 Thomas J. McCabe 于 1976 年提出的,用于衡量代码中独立执行路径的数量。每个独立路径都对应一种可能的程序执行流。圈复杂度的计算基于程序的控制流图,其中节点表示代码块,边表示控制流的转移。

公式:M = E - N + 2P其中:

tag desc
M 圈复杂度
E 控制流图中的边数(Edges)
N 控制流图中的节点数(Nodes)
P 是连通分量数(通常为 1)

bigger

图示有流程图的函数,可以计算其圈复杂度为V(G) = 9(E)-8(N)+2*1 = 3

1.2 常见控制结构的圈复杂度 #

tag desc
if 每个条件增加 1
for/while 每个循环增加 1
switch-case 每个 case 或 default 增加 1
三元操作符 每个三元表达式增加 1

圈复杂度越高,代码的测试路径越多,意味着需要编写更多的测试用例来覆盖所有可能的执行路径。

1.3 优缺点 #

  1. 优点:圈复杂度的度量方法简单明了,适合衡量代码逻辑分支的数量,从而帮助开发者了解代码的潜在风险。
  2. 缺点:它并不考虑代码的实际可读性和理解难度,仅仅反映了代码的逻辑复杂度。因此,圈复杂度只能反映代码的某个维度,不能完全代表开发者的工作量。

1.4 圈复杂度的行业标准 #

Cyclomatic Complexity Code Quality Readability Maintainability
1-10 Clear and well-structured High Low
11-20 Somewhat complex Medium Moderate
21-50 Complex Low Difficult
51+ Very complex Poor Very difficult

二、认知复杂度(Cognitive Complexity) #

2.1 定义与目标 #

认知复杂度是由 SonarSource 提出的概念,旨在衡量代码对于人类理解的难度。与圈复杂度不同,认知复杂度更关注代码的可读性和理解的心理负担,而不仅仅是控制流的复杂性。其核心思想是:当代码不符合自然的思考流程或使人类阅读时需要跳转时,认知复杂度会增加。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
function sumOfPrimes(max) {
  let total = 0;
  OUT: for (let i = 1; i <= max; ++i) { // +1
    for (let j = 2; j < i; ++j) { // +2
      if (i % j === 0) { // +3
        continue OUT; // +1
      }
    }
    total += i;
  }
  return total;
}
// Cyclomatic Complexity 7

function getWords(number) {
  switch (number) { // +1
    case 1:
      return "one";
    case 2:
      return "a couple";
    case 3:
      return "a few";
    default:
      return "lots";
  }
}
// Cyclomatic Complexity 1

2.2 认知复杂度的计算原则 #

基础层次的理解不增加复杂度:如一个简单的顺序语句不会增加认知复杂度。 每个控制结构增加复杂度:如 if、for、while 等。 嵌套结构增加额外的复杂度:嵌套的 if 或 for 等使得理解难度更大,因此额外增加复杂度。 过度的跳转、分支和中断会增加复杂度:如 break、continue、goto 等。

2.3 优缺点及行业标准 #

  1. 优点:认知复杂度侧重于开发者的心理负担,提供了更贴近实际代码可读性和维护性的衡量标准。
  2. 缺点:认知复杂度的计算较为主观,并且依赖于特定的工具支持(如 SonarQube 等)。
Cognitive Complexity Code Quality Readability Maintainability
1-5 Simple and easy to follow High Easy
6-10 Somewhat complex Medium Moderate
11-20 Complex Low Difficult
21+ Very complex Poor Very difficult

其他复杂度度量方法 #

除了圈复杂度和认知复杂度,以下几种度量方法也可以用于衡量代码的复杂性:

  1. Halstead 复杂度 度量是基于程序中的操作符和操作数的数量,计算出程序的体积、难度和开发工作量。它通过衡量程序的符号复杂性来分析代码。
  2. NPath 复杂度 代码中执行路径的数量,计算方法是通过分析条件语句和循环中的分支,得出程序中有多少可能的执行路径。其结果反映了测试代码所需的可能路径总数。

参考:

https://clean99.github.io/2023/03/04/Introducing-Code-Complexity-Metric-Cognitive-Complexity/

https://image-1252065837.picbj.myqcloud.com/cyleft/Cognitive_Complexity_Sonar_Guide_2023.pdf