【问题标题】:How to reduce cyclomatic complexity on this c# method?如何降低此 c# 方法的圈复杂度?
【发布时间】:2021-06-07 14:13:09
【问题描述】:

我目前正在为我的 c# 类做一个项目。我们的老师给了我们一些必须遵守的代码度量限制,其中之一就是圈复杂度。现在他下面的方法的复杂度是5,但它必须是4。有什么办法可以改进吗?

方法我说的是:

private bool MethodName()
    {
        int counter = 0;

        for (int k = 0; k < 8; k++)
        {
            for (int j = 0; j < 3; j++)
            {
                if (class1.GetBoard()[array1[k, j, 0], array1[k, j, 1]] == player.WhichPlayer()) counter++;
            }

            if (counter == 3) return true;
            else counter = 0;
        }
        return false;
    }

【问题讨论】:

  • 可以使用 int counter = 0;在第一个 for 循环内。然后,您可以删除else counter = 0;
  • 写一些难以理解的linq! (这实际上可能是这里要做的事情。)
  • 更严重的是,解决这个问题的标准方法(除非你可以这样简化代码)是使用一些块(如内部 for 循环)来分隔函数。
  • 不知道该指标,但看起来class1.GetBoard() 不依赖于jk,所以我肯定会只调用一次(!)外部 for 循环,而不是调用它 8*3inside for 循环。 player.WhichPlayer() 也是如此。
  • 一个微优化可以基于counter实际上只在外循环中本地使用;将定义和初始化移到其顶部,从而避免 else 分支。

标签: c# cyclomatic-complexity


【解决方案1】:

我可以包装条件来减少它。例如

private bool MethodName()
    {
        for (int k = 0; k < 8; k++)
        {
            bool b = true;
            for (int j = 0; j < 3; j++)
            {
                b &= class1.GetBoard()[array1[k, j, 0], array1[k, j, 1]] == player.WhichPlayer();
            }

            if (b) return true;
        }
        return false;
    }

【讨论】:

  • 将 bool 转换为 int 以降低圈复杂度......它可能会这样做,但会使代码变得很多。不太清楚。除非我有非常充分的理由,否则我不会这样做。
  • 我的意思是,从技术上讲,您也可以删除if (counter == 3) return true 条件并添加goto 语句...你会吗?
  • @RodrigoRodrigues,没有时间编写干净的代码。无需转换。无论如何,圈复杂度很少用于实际项目中
  • @mandarancza 这是逻辑与。这意味着只有在所有条件都为真的情况下,“b”才会为真。任何错误的选项都会给你带来错误的结果。
  • @BorisSokolov 感谢您的解释,这似乎是更好的选择,谢谢
【解决方案2】:

对于 OP(似乎刚刚开始编程):

很高兴你得到了降低方法的圈复杂度的任务,知道它是什么以及如何将其作为一般做法保持在低水平是件好事。

但是,请尽量不要对这些指标过于狂热。让代码尽可能简单更有价值,易于推理和快速理解,并且只在分析应用程序并了解最重要的位置后才担心指标。

对于更有经验的编码人员:

这个简单的问题让我想起了 1968 年 Dijkstra 和 ACM 期刊上的其他人之间的 very famous discussion。虽然我倾向于在这件事上与他保持一致,但这是一个非常合理的answer from Frank Rubin

Frank 基本上主张“优雅”可以多次来自代码清晰,而不是任何其他实践指标。那时,讨论是过度使用当时流行语言的goto 声明。今天,讨论围绕圈复杂性、简洁性、oop 等。

在我看来,底线是:

  • 了解您的工具
  • 代码清晰
  • 尝试在第一遍编写高效的代码,但不要想太多
  • 分析您的代码并决定在哪里花费更多时间

回到问题

问题中提出的实现在我的 Visual Studio Analyzer 中获得了以下分数:

循环。编译:5;可维护性:67

@Boris 提供的 sn-p 得到了这个:

循环。编译:4;可维护性:68

虽然圈复杂度有所提高,但可维护性指标基本保持不变。就个人而言,我认为大多数时候后一个指标更有价值。

只是为了好玩,让我们看看类似于 Frank Rubin 提出的使用可怕的 goto 语句的解决方案的样子:

private bool MethodName() {
  for (int k = 0; k < 8; k++) {
    for (int j = 0; j < 3; j++) {
      if (watheverTestCondition(k, j) is false) goto reject;
    }
    // condition is true for all items in this row
    return true; 
    // if condition is false for any item, go straight to this line
    reject:;
  }
  return false;
}

老实说,我认为这是最清晰、最简单和最高效的实现。我一般推荐goto 作为代码功能吗? 。在这种特定情况下,它是否完美而顺畅? 是的。那么指标呢?

循环。编译:4;可维护性:70


奖金

只是因为如果我不告诉我就无法入睡,这就是你将如何在现实生活中实现它

obj.Any(row => row.All(watheverTestCondition));

循环。编译:1;可维护性:80

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-06-13
    • 2020-10-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多