你觉得圈复杂度是一个有用的衡量标准吗?

Posted

技术标签:

【中文标题】你觉得圈复杂度是一个有用的衡量标准吗?【英文标题】:Do you find cyclomatic complexity a useful measure? 【发布时间】:2010-10-19 06:04:39 【问题描述】:

我一直在尝试测量大型代码库的圈复杂度。

圈复杂度是通过程序源代码的线性独立路径的数量,有很多免费工具可供您选择。

结果很有趣,但并不令人惊讶。也就是说,我知道毛最多的部分实际上是最复杂的(评分> 50)。但我发现有用的是每个方法都分配了一个具体的“坏”数字,作为我在决定从哪里开始重构时可以指出的东西。

你使用圈复杂度吗?你发现的最复杂的代码是什么?

【问题讨论】:

【参考方案1】:

我们毫不留情地重构,并使用圈复杂度作为衡量我们“命中列表”代码的指标之一。 1-6 我们不会标记复杂性(尽管它可能会因其他原因受到质疑),7-9 是有问题的,除非另有证明,否则任何超过 10 的方法都被认为是不好的。

我们见过的最糟糕的情况是 87 来自我们不得不接管的一些遗留代码中的一个巨大的 if-else-if 链。

【讨论】:

87?这是对 Arrow 反模式的一个非常彻底的实现……诚挚的哀悼。 所以基本上一个包含 10 个连续 if 语句的高度顺序函数会通过测试? 今晚我刚刚研究了 CC,因为我试图为项目的代码清理提供有效的攻击计划。最严重的违规者是单个方法的 450 和一个类的 1,289(不,我没有写任何内容)。好游戏。叹息………… 刚加入一个公司,发现一个windows窗体有1518【参考方案2】:

实际上,圈复杂度可以在方法级别阈值之外使用。对于初学者来说,一个复杂度高的大方法可能会分解成几个复杂度较低的小方法。但它真的改进了代码库吗?当然,通过所有这些方法名称,您可能会获得更好的可读性。但总的条件逻辑并没有改变。并且总的条件逻辑通常可以减少replacing conditionals with polymorphism。

我们需要一个不会仅仅因为方法分解而变绿的指标。我称之为CC100。

CC100 = 100 *(代码库的总圈复杂度)/(总代码行数)

【讨论】:

但是可测试性有所提高:单独的方法(原则上)可以单独测试,即使逻辑没有改变。当然,如果这些方法还依赖于很多全局状态,这并不成立,但这本身就是一个问题。 +1 用于指向有趣幻灯片的超链接。我最近在这个问题上花了一些想法,很高兴能找到更多关于它的材料。 用多态性代替条件句可能会降低圈复杂度,但也会降低其局部可理解性。 @Wolf OO-code 更多的是通过其接口(封装)而不是实现来理解 - 至少在使用点(方法调用)。 @ottodidakt 是的,我似乎没有真正理解你的意思——现在你似乎批评了经典 CC 指标的使用,声称 CC100 有助于检测过于复杂的代码?【参考方案3】:

它对我的用处与 big-O 的用处相同:我知道它是什么,并且可以使用它来直觉地判断一种方法是好是坏,但我不需要计算它适用于我编写的每个函数。

我认为在大多数情况下,更简单的指标(例如 LOC)至少也一样好。如果一个功能不能在一个屏幕上显示,那么它的简单程度几乎无关紧要。如果一个函数接受 20 个参数并产生 40 个局部变量,那么它的圈复杂度是否为 1 无关紧要。

【讨论】:

我想说所有这些参数和局部变量都是为了逻辑流。因此,它们适用于 CC。只是在我的脑海中思考。【参考方案4】:

除非有一个工具可以很好地与 C++ 模板和元编程技术一起工作,否则它对我的情况没有多大帮助。无论如何只要记住这一点

"不是所有可以计算的东西都可以 测量的,而不是所有可以测量的东西 被测量计数" 爱因斯坦

因此,请记住也要通过人工过滤传递任何此类信息。

【讨论】:

【参考方案5】:

我们最近开始使用它。我们使用 NDepend 做一些静态代码分析,它测量圈复杂度。我同意,这是一种识别重构方法的好方法。

遗憾的是,我们已经看到我们的海外开发人员创建的一些方法的 # 超过 200。

【讨论】:

在早年,我记得看过300多个。 我的一个同事遇到过1000多例。 超过 9000 !!!!!! ....对不起,忍不住。任何超过 200 的东西都会令人难以置信【参考方案6】:

当你看到它时,你就会知道它的复杂性。这种工具的主要用途是标记代码中没有引起您注意的部分。

【讨论】:

还有一件很有意思的事:经常修改的代码复杂度很高,是bug的滋生地。因此,自动计算复杂性可能是一件好事。【参考方案7】:

我经常测量我的代码的圈复杂度。我发现它可以帮助我发现做得太多的代码区域。使用工具指出我的代码中的热点比阅读数千行代码试图找出哪些方法不遵循 SRP 所花费的时间要少得多。

但是,我发现当我对其他人的代码进行圈复杂度分析时,当我发现圈复杂度为 100 的代码时,通常会导致沮丧、焦虑和普遍愤怒的感觉。是什么迫使人们编写包含数千行代码的方法?!

【讨论】:

我见过你所说的一些巨大的方法,通常是关于灭火。一旦大火熄灭,就没有理由重构(这该死的!),现在这段代码要大得多,几周/几个月后又会大火。【参考方案8】:

这对于帮助确定重构候选者很有帮助,但保持判断力很重要。我支持 kenj0418 的修剪指南范围。

【讨论】:

【参考方案9】:

有一个名为 CRAP4J 的 Java 度量标准,它根据经验结合了圈复杂度和 JUnit 测试覆盖率,从而得出一个单一的度量标准。他一直在做研究,试图改进他的经验公式。我不确定它有多普遍。

【讨论】:

【参考方案10】:

圈复杂度只是所谓的制造复杂度的一种组合。前段时间,我写了一篇文章,总结了代码复杂度的几个维度: Fighting Fabricated Complexity

需要工具来有效地处理代码复杂性。用于 .NET 代码的工具 NDepend 可让您分析代码复杂性的多个维度,包括代码指标,例如: 圈复杂度、嵌套深度、方法缺乏凝聚力、测试覆盖率……

包括依赖关系分析和包括一种语言 (Code Query Language) 专门用于询问,我的代码中有什么复杂的,以及编写规则?

【讨论】:

【参考方案11】:

是的,我们使用它,我发现它也很有用。我们有一个庞大的遗留代码库要驯服,我们发现了惊人的高圈复杂度。 (一种方法中有 387 个!)。 CC 将您直接指向值得重构的领域。我们在 C++ 代码上使用 CCCC。

【讨论】:

【参考方案12】:

我已经有一段时间没有使用它了,但在之前的项目中,它确实有助于识别其他人代码中的潜在问题(当然不是我的!)

在找到要检查的区域后,我很快发现了许多逻辑问题(你相信还有很多 GOTOS!)和一些非常奇怪的 WTF 代码。

圈复杂度非常适合显示可能正在做很多事情并因此打破单一责任主体的领域。理想情况下,这些应该分解为多个函数

【讨论】:

【参考方案13】:

恐怕对于我最想要像 LPC 这样的指标的项目语言,实际上并没有很多免费的工具可以用来生成它。所以不,对我没那么有用。

【讨论】:

嘿。有人知道这个故事。【参考方案14】:

+1 表示 kenj0418 的命中列表值。

我见过的最差的是 275。还有几个超过 200 的 CC,我们能够重构为更小的 CC;他们仍然很高,但这让他们更进一步。我们对 275 野兽的运气并不好——它(可能仍然是)一个过于复杂的 if 和 switch 语句网络。当他们决定重建系统时,唯一真正的价值是作为一个步骤。

我比较满意的高 CC 例外是工厂; IMO,它们应该具有较高的 CC,但前提是它们只进行简单的对象创建和返回。

【讨论】:

【参考方案15】:

在understanding 是什么意思之后,我现在已经开始在“试用”的基础上使用它。到目前为止,我发现它很有用,因为通常高 CC 与 Arrow Anti-Pattern 齐头并进,这使得代码更难阅读和理解。我还没有一个固定的数字,但 NDepend 会针对 5 以上的所有内容发出警报,这看起来是研究方法的良好开端。

【讨论】:

以上是关于你觉得圈复杂度是一个有用的衡量标准吗?的主要内容,如果未能解决你的问题,请参考以下文章

前端代码质量-圈复杂度原理和实践

OO游记之三月篇

前端代码质量-圈复杂度原理和实践

dotnet 代码优化 聊聊逻辑圈复杂度

.NET 代码优化 聊聊逻辑圈复杂度

软件质量管理-代码质量与规范