【问题标题】:How does Sonar calculate the cyclomatic complexity?Sonar 如何计算圈复杂度?
【发布时间】:2018-02-22 06:36:59
【问题描述】:

Sonar 给了我以下圈复杂度数:22。

对于以下程序:

private static SomeDto checkSomething(AnotherDto anotherDto, String reference)
{
SomeDto someDto = new SomeDto();

// condition 1
if (!someDto.getA())
    return new SomeDto("bla1", "blabla");

// condition 2
if (someDto.getName2() == null || checkSurName(anotherDto.getName()))
    return new SomeDto("bla2", "blabla");

// condition 3
if (someDto.getName3() == null || checkSurName(anotherDto.getName()))
    return new SomeDto("bla3", "blabla");

// condition 4
if (someDto.getName4() == null && checkSurName(anotherDto.getName()))
    return new SomeDto("bla4", "blabla");

// condition 5
if (someDto.getName5() == null || checkSurName(anotherDto.getName()))
    return new SomeDto("bla5", "blabla");

// condition 6
if (someDto.getName6() == null && checkSurName(anotherDto.getName()))
    return new SomeDto("bla6", "blabla");

// condition 7
if (someDto.getName7() == null && checkSurName(anotherDto.getName()))
    return new SomeDto("bla7", "blabla");

// condition 8
if (someDto.getName8() == null && checkSurName(anotherDto.getName()))
    return new SomeDto("bla8", "blabla");

// condition 9
if (someDto.getName9() == null && checkSurName(anotherDto.getName()))
    return new SomeDto("bla9", "blabla");

// condition 10
if (someDto.getName10() == null && checkSurName(anotherDto.getName()))
    return new SomeDto("bla10", "blabla");

// condition 11
if (someDto.getName11() == null && checkSurName(anotherDto.getName()))
    return new SomeDto("bla11", "blabla");

return someDto;
}    

我得到的问题如下:

“此方法“checkSomething”的圈复杂度为 22,大于授权的 12。

我的问题是: 考虑 Mac Cabe 公式 v(g) = e - n + 2,Sonar 如何达到 22 的个数?

地点:

e = 边数

n = 节点数

这个方法有多少边和节点? 这个方法的控制流程是什么?

我们使用的是 SonarQube 版本 6.3(内部版本 19869)。

【问题讨论】:

  • edit您的问题包括相关分析仪的名称和版本
  • 你能发一个具体的if (condition*) 吗?
  • 是的。 if (anotherDto.getName() == null || !checkSurName(anotherDto.getName())) return new SomeDto("bla10", "blabla");
  • 我编辑了方法的代码。条件 (ifs) 包含 ||或 && 操作数,第一个除外。

标签: java sonarqube graph-algorithm checkstyle cyclomatic-complexity


【解决方案1】:

SonarQube documentation for the latest version 清楚地说明了它如何计算圈复杂度:

Complexity(复杂度)是计算的圈复杂度 基于通过代码的路径数。每当控制 函数流分裂,复杂度计数器增加 一。每个函数的最小复杂度为 1。这个计算 由于关键字和功能会因语言而异。

如果您打开该段落下方的“特定于语言的详细信息”,Java 行将如下所示。

增加复杂性的关键字:if、for、while、case、catch、 抛出, &&, ||, ?

由于在提问时 OP 使用的是 6.3 版本,因此我还检查了我能找到的 documentation for the oldest version,即 6.7。到那时,计算方式略有不同。

增加复杂性的关键字:if、for、while、case、catch、 throw、return(这不是方法的最后一条语句)、&&、||、?

注意事项:

else、default 和 finally 关键字不会增加复杂性。

一个简单的方法,带有一个 switch 语句和一个巨大的 case 块 语句可以具有令人惊讶的高复杂度值(仍然具有 将 switch 块转换为等效块时的值相同 if 语句序列)。

示例:以下方法的复杂度为 5

public void process(Car myCar){          // +1
    if(myCar.isNotMine()){               // +1
         return;                         // +1
    }
    car.paint("red");
    car.changeWheel();
    while(car.hasGazol() && car.getDriver().isNotStressed()){   // +2
         car.drive();
    }
    return; }

【讨论】:

    【解决方案2】:

    好吧,经过进一步调查并根据这个link(checkstyle 工具),我得出结论,McCabe 公式并没有真正应用于计算 Java 程序中的圈复杂度。

    复杂度等于决策点的数量 + 1 个决策点:if、while、do、for、?:、catch、switch、case 语句以及运算符 && 和 ||在目标体内。

    因此,如果我将此规则应用于前面的示例代码:

    private static SomeDto checkSomething(AnotherDto anotherDto, String reference)  // 1
    {
    SomeDto someDto = new SomeDto();
    
    // condition 1
    if (!someDto.getA())                                                             // 2
    return new SomeDto("bla1", "blabla");
    
    // condition 2
    if (someDto.getName2() == null || checkSurName(anotherDto.getName()))             // 4
    return new SomeDto("bla2", "blabla");
    
    // condition 3
    if (someDto.getName3() == null || checkSurName(anotherDto.getName()))             // 6
    return new SomeDto("bla3", "blabla");
    
    // condition 4
    if (someDto.getName4() == null && checkSurName(anotherDto.getName()))             // 8
    return new SomeDto("bla4", "blabla");
    
    // condition 5
    if (someDto.getName5() == null || checkSurName(anotherDto.getName()))              // 10
    return new SomeDto("bla5", "blabla");
    
    // condition 6
    if (someDto.getName6() == null && checkSurName(anotherDto.getName()))              // 12
    return new SomeDto("bla6", "blabla");
    
    // condition 7
    if (someDto.getName7() == null && checkSurName(anotherDto.getName()))              // 14
    return new SomeDto("bla7", "blabla");
    
    // condition 8
    if (someDto.getName8() == null && checkSurName(anotherDto.getName()))              // 16
    return new SomeDto("bla8", "blabla");
    
    // condition 9
    if (someDto.getName9() == null && checkSurName(anotherDto.getName()))              // 18
    return new SomeDto("bla9", "blabla");
    
    // condition 10
    if (someDto.getName10() == null && checkSurName(anotherDto.getName()))             // 20
    return new SomeDto("bla10", "blabla");
    
    // condition 11
    if (someDto.getName11() == null && checkSurName(anotherDto.getName()))             // 22
    return new SomeDto("bla11", "blabla");
    
    return someDto;
    }    
    

    如果我错了,请纠正我。 不应该至少考虑返回语句(方法的最后一个语句除外)吗?

    无论如何,结果数字 22 是有道理的。这种方法有大量连续的“if”条件,应该对其进行一些处理以提高其可维护性。

    【讨论】:

    • 是的。声纳不要使用圈复杂度,它有它自己的方式。 docs.sonarqube.org/display/SONAR/Metrics+-+Complexity
    • 我猜“if”语句(if + else)为 2,if 语句为 11 * 每个 if = 22 为 2
    • @LucasLiu 实际上它确实按照您分享的文档链接中所述:“复杂度(complexity)它是根据通过代码的路径数计算的圈复杂度。”跨度>
    猜你喜欢
    • 2023-03-27
    • 1970-01-01
    • 1970-01-01
    • 2023-03-20
    • 1970-01-01
    • 2011-10-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多