应懂的圈复杂度

什么是圈复杂度

圈复杂度 (Cyclomatic complexity)(也称为条件复杂度 /循环复杂度)是一种代码复杂度的衡量标准,在1976年由Thomas J. McCabe, Sr. 提出。

圈复杂度可以用来衡量一个模块判定结构的复杂程度,数量上表现为独立现行路径条数,也可理解为覆盖所有的可能情况最少使用的测试用例数。即合理的预防错误所需测试的最少路径条数。

圈复杂度大说明程序代码可能质量低且难于测试和维护,根据经验,程序的可能错误和高的圈复杂度有着很大关系。

圈复杂度优劣

先看圈复杂度代码质量 以及测试维护成本之间的一个关系:

圈复杂度 代码状况 可测性 维护成本
1-10 清晰、结构化
10-20 复杂
20-30 非常复杂
>30 不可读 不可测 非常高

可以看到当圈复杂度,在 1-10 之间的时候,代码是清晰,结构化的。可测试性比较高,维护成本也比较低。随着圈复杂度的升高,代码的状况开始恶化,当大于 30 的时候,代码已经逐步变为不可读,维护成本非常高。

优势:

  • 维护性高:通过控制代码圈复杂度,可以提高代码的可维护性。简单的代码结构更易于理解和修改。
  • 减少错误引入的可能性:低圈复杂度的代码通常更容易测试,从而减少引入错误的可能性。
  • 团队协作更顺畅:简单的代码结构使得团队成员更容易理解彼此的工作,降低了协作的难度。

劣势:

  • 可能引入过度的拆分:过于强调低圈复杂度有时可能导致过度拆分函数或模块,使得代码过于分散,反而降低了可读性。
  • 不完全反映代码质量:虽然圈复杂度可以衡量代码的复杂性,但它并不能完全反映代码的质量。有些代码可能具有较低的圈复杂度,但仍然存在其他问题,如可读性差、缺乏注释等。

圈复杂度的计算方法

计算方法有点边计算法、节点判定法等。

点边计算法

计算公式:
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> V ( G ) = E − N + 2 V(G)=E-N+2 </math>V(G)=E−N+2

其中:

  • ( V(G) ) 是圈复杂度;
  • ( E ) 是图中的边数(程序图中的分支数);
  • ( N ) 是图中的节点数(基本块数,即顺序执行的代码块)。

图示:

其中公式之中的 E 指的是控制流图中边的数量,N 指的是控制流图中的节点数量。上图就是控制流图。那我们可以计算一下,这个控制流图的圈复杂度是:4-4+2=2.

节点判定法

一种更为直观的计算方法,因为圈复杂度实际上体现了 "判定条件" 的数量,所以圈复杂度实际上就是等于判定节点的数量再加上 1。

计算公式:
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> V ( G ) = P + 1 V(G)=P+1 </math>V(G)=P+1

  • ( V(G) ) 是圈复杂度;
  • 判定节点 (P) 指的是我们常用的分支语句。例如 if 语句、while 语句、case 语句等。

知道了圈复杂度的计算方式,那么如何降低圈复杂度呢?

如何降低圈复杂度

使用卫语句

圈复杂度的一个因素就是分支语句多。卫语句的原则是,如果某个条件极其罕见,就应该单独检查该条件,并在该条件为真时,立刻返回。采用卫语句的方式,来减少分支语句。让代码更清晰。

简化条件

有相同逻辑代码进行条件合并输出,减少条件判断代码,提升可读性。

提取单一功能函数

函数的复杂性是导致圈复杂度升高的另一个常见原因。当一个函数包含过多的逻辑和操作时,它往往难以理解和维护。为了降低圈复杂度,可以将复杂的函数拆分成多个小函数,每个函数只负责一个特定的任务。这样可以提高代码的可读性和可维护性,并且使得每个函数的圈复杂度更低。

检查圈复杂度

VSCode显示圈复杂度

只需在VSCode中安装下图所示插件。 效果:

图中显示fn方法的圈复杂度为4。

eslint检查

使用 eslint 帮助检查代码的圈复杂度,当超出了某个值就会报错。

js 复制代码
rules: {
    complexity: ['error', 15], // 设置圈复杂度阈值为15
}

最后

圈复杂度是一个重要的代码质量度量指标,通过对程序流程的路径数量进行计算,反映了代码的复杂性和可维护性。通过控制圈复杂度,我们可以提高代码的可读性、可维护性,减少错误的引入。然而,需要在实际项目中权衡各种因素,避免过度强调圈复杂度而牺牲了其他方面的质量。

相关推荐
fruge11 分钟前
纯css制作声波扩散动画、js+css3波纹催眠动画特效、【css3动画】圆波扩散效果、雷达光波效果完整代码
javascript·css·css3
neter.asia20 分钟前
vue中如何关闭eslint检测?
前端·javascript·vue.js
光影少年40 分钟前
vue2与vue3的全局通信插件,如何实现自定义的插件
前端·javascript·vue.js
Rattenking1 小时前
React 源码学习01 ---- React.Children.map 的实现与应用
javascript·学习·react.js
熊的猫2 小时前
JS 中的类型 & 类型判断 & 类型转换
前端·javascript·vue.js·chrome·react.js·前端框架·node.js
别拿曾经看以后~3 小时前
【el-form】记一例好用的el-input输入框回车调接口和el-button按钮防重点击
javascript·vue.js·elementui
川石课堂软件测试4 小时前
性能测试|docker容器下搭建JMeter+Grafana+Influxdb监控可视化平台
运维·javascript·深度学习·jmeter·docker·容器·grafana
JerryXZR4 小时前
前端开发中ES6的技术细节二
前端·javascript·es6
problc4 小时前
Flutter中文字体设置指南:打造个性化的应用体验
android·javascript·flutter
Gavin_9154 小时前
【JavaScript】模块化开发
前端·javascript·vue.js