【问题标题】:Early return statements and cyclomatic complexity早期返回语句和圈复杂度
【发布时间】:2014-09-15 06:37:06
【问题描述】:

我更喜欢这种早期回报的写作风格:

public static Type classify(int a, int b, int c) {
    if (!isTriangle(a, b, c)) {
        return Type.INVALID;
    }
    if (a == b && b == c) {
        return Type.EQUILATERAL;
    }
    if (b == c || a == b || c == a) {
        return Type.ISOSCELES;
    }
    return Type.SCALENE;
}

不幸的是,每条return 语句都会增加 Sonar 计算的圈复杂度指标。考虑这个替代方案:

public static Type classify(int a, int b, int c) {
    final Type result;
    if (!isTriangle(a, b, c)) {
        result = Type.INVALID;
    } else if (a == b && b == c) {
        result = Type.EQUILATERAL;
    } else if (b == c || a == b || c == a) {
        result = Type.ISOSCELES;
    } else {
        result = Type.SCALENE;
    }
    return result;
}

Sonar 报告的后一种方法的圈复杂度比第一种低 3。有人告诉我,这可能是 CC 指标实施错误的结果。或者声纳是正确的,这真的更好吗?这些相关问题似乎不同意这一点:

https://softwareengineering.stackexchange.com/questions/118703/where-did-the-notion-of-one-return-only-come-from

https://softwareengineering.stackexchange.com/questions/18454/should-i-return-from-a-function-early-or-use-an-if-statement

如果我添加对更多三角形类型的支持,return 语句加起来会在指标上产生显着差异并导致声纳违规。我不想在该方法上粘贴// NOSONAR,因为这可能会通过将来添加到该方法中的新功能/错误来掩盖其他问题。所以我使用第二个版本,即使我不是很喜欢它。有没有更好的方法来处理这种情况?

【问题讨论】:

  • 根据en.wikipedia.org/wiki/Cyclomatic_complexity,CC 是通过函数的线性独立路径的数量,在这两种情况下都是 4. Sonar 告诉你一些不同的东西吗?
  • 是的。 Sonar 为每个 return 语句添加 +1。这是squid:MethodCyclomaticComplexity 规则:dev.eclipse.org/sonar/rules/show/… 较早的规则(但现在不赞成使用 squid)没有此约束:dev.eclipse.org/sonar/rules/show/…
  • 所以这个问题的真正意思是“如何防止声纳计算错误的 CC” - 这不适合这个网站。这样的问题最好放在stackoverflow上。我将此问题标记为迁移。
  • 你在这里做了两件事。您有条件逻辑,但也有保护条款。我倾向于尊重单一职责的概念,因为总的来说它使代码更具可读性,除了函数开头的保护子句。
  • 之前我问了一个类似的问题 (stackoverflow.com/questions/23381265/…),结果发现 SonarQube 报告的复杂性混合了“循环复杂性”和“基本复杂性”。

标签: cyclomatic-complexity sonarqube


【解决方案1】:

不是真正的答案,但评论太长了。

这个 SONAR 规则似乎被彻底打破了。你可以重写

b == c || a == b || c == a

作为

b == c | a == b | c == a

并在这个奇怪的游戏中获得两分(甚至可能会加快一些速度,因为分支很昂贵;但这取决于 JITc,无论如何)。

old rule 声称,圈复杂度与测试次数有关。 new one 没有,这是一件好事,因为显然你的两个 sn-ps 的有意义测试的数量是完全相同的。

有没有更好的方法来处理这种情况?

实际上,我确实有一个答案:对于每次提前返回,请使用 | 而不是 || 一次。 :D

现在说真的:有一个错误请求annotations 允许禁用单个规则,该规则被标记为已修复。我没有再看下去了。

【讨论】:

    【解决方案2】:

    您的问题与https://jira.codehaus.org/browse/SONAR-4857 有关。目前,所有 SonarQube 分析仪都在混合圈复杂性和本质复杂性。从理论上讲,return 语句不应该增加 cc,这种变化将发生在 SQ 生态系统中。

    【讨论】:

    • 谢谢!什么时候有什么想法?
    • 今年年底肯定,但暂时不能更准确。
    【解决方案3】:

    由于问题还涉及作为编码风格的早期返回语句,因此考虑大小对返回风格的影响会有所帮助。如果方法或函数很小,比如不到 30 行,提前返回是没有问题的,因为任何阅读代码的人都可以一眼看到整个方法,包括所有的返回。在较大的方法或函数中,提前返回可能是无意中为读者设置的陷阱。如果提前返回发生在读者正在查看的代码之上,而读者不知道返回在上面或者忘记了它在上面,那么读者就会误解代码。生产代码可能太大而无法在一个屏幕上显示。

    因此,在复杂性出现问题的情况下,管理复杂性代码库的人应该考虑方法大小。如果代码占用的屏幕不止一个屏幕,则更迂腐的返回风格可能是合理的。如果方法或函数很小,不用担心。

    (我使用 Sonar 并遇到过同样的问题。)

    【讨论】:

    • 我强烈反对。你是对的,在一个小方法中,早期的回报是好的。实际上,在一个小方法中,一切都很好。在一个大的方法中,一切都是问题,这就是重点。为什么有人写超过 30 行的方法???看看我昨天半睡半醒写的代码,我可以看到那里有些乱七八糟的东西是大方法(大约 30 行)。 摆脱大方法,你会没事的。 如果代码占用多个屏幕,请重构。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-05-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多