【问题标题】:How do I reduce the cyclomatic complexity?如何降低圈复杂度?
【发布时间】:2020-06-13 20:28:19
【问题描述】:

感谢您阅读我的问题。 我目前正在 Coursera 上上 Java 课程,并被要求为这项作业编写一个关于扫雷的程序。我的代码创建了正确的结果,但我的成绩被大大扣分了,因为根据自动评分器的说法,我的代码“过于复杂,圈复杂度为 60”。我知道有太多的条件和循环,但我很难让它变得更简单。

这是我的代码。它需要 3 个整数命令行参数 m、n 和 k 来创建一个 m×n 网格,其中 k 个地雷位于随机位置。我使用“5”而不是“”来标记地雷,因为瓷砖中的最高数字是 4(因为瓷砖有 4 个面)。如果两个地雷并排放置,则可能会在其标记“5”中添加额外的值。因此,当我将所有值 >= 5 打印出来时,我将它们变为“”。每个值用两个空格分隔。

public class Minesweeper {
  public static void main(String[] args) {
    int m = Integer.parseInt(args[0]);
    int n = Integer.parseInt(args[1]);
    int k = Integer.parseInt(args[2]);
    int[][] mine = new int[m][n];
    //put the mines
    for(int z = 0; z < k; z++) {
      int randomX = (int) (Math.random() * m);
      int randomY = (int) (Math.random() * n);
      mine[randomX][randomY] = 5; 
    }

    for(int y = 0; y < n; y++) {
      for(int x = 0; x < m; x++) {
        //first row of the grid
        if(y == 0) {
          //upper left corner
          if(x == 0) {
            if(mine[x + 1][y] >= 5) {
              mine[x][y] += 1;
            }
            if(mine[x][y + 1] >= 5) {
              mine[x][y] += 1;
            }
          }
          //upper right corner
          else if(x == m - 1) {
            if(mine[x - 1][y] >= 5) {
              mine[x][y] += 1;
            }
            if(mine[x][y + 1] >= 5) {
              mine[x][y] += 1;
            } 
          }
          //mid of first row
          else {
            if(mine[x - 1][y] >= 5) {
              mine[x][y] += 1;
            }
            if(mine[x + 1][y] >= 5) {
              mine[x][y] += 1;
            }
            if(mine[x][y + 1] >= 5) {
              mine[x][y] += 1;
            } 
          }
        }
        //mid rows
        else if(y > 0 && y < n - 1) {
          //left side
          if(x == 0) {
            if(mine[x][y - 1] >= 5) {
              mine[x][y] += 1;
            }
            if(mine[x][y + 1] >= 5) {
              mine[x][y] += 1;
            }
            if(mine[x + 1][y] >= 5) {
              mine[x][y] += 1;
            }
          }
          //right side
          else if(x == m - 1) {
            if(mine[x][y - 1] >= 5) {
              mine[x][y] += 1;
            }
            if(mine[x][y + 1] >= 5) {
              mine[x][y] += 1;
            }
            if(mine[x - 1][y] >= 5) {
              mine[x][y] += 1;
            }
          }
          //mid
          else {
            if(mine[x][y - 1] >= 5) {
              mine[x][y] += 1;
            }
            if(mine[x][y + 1] >= 5) {
              mine[x][y] += 1;
            }
            if(mine[x - 1][y] >= 5) {
              mine[x][y] += 1;
            }
            if(mine[x + 1][y] >= 5) {
              mine[x][y] += 1;
            }
          } 
        }
        //bottom row
        else if(y == n - 1) {
          //bottom left corner
          if(x == 0) {
            if(mine[x + 1][y] >= 5) {
              mine[x][y] += 1;
            }
            if(mine[x][y - 1] >= 5) {
              mine[x][y] += 1;
            }
          }
          //bottom right corner
          else if(x == m - 1) {
            if(mine[x - 1][y] >= 5) {
              mine[x][y] += 1;
            }
            if(mine[x][y - 1] >= 5) {
              mine[x][y] += 1;
            }
          }
          //middle of the bottom row
          else {
            if(mine[x + 1][y] >= 5) {
              mine[x][y] += 1;
            }
            if(mine[x - 1][y] >= 5) {
              mine[x][y] += 1;
            }
            if(mine[x][y - 1] >= 5) {
              mine[x][y] += 1;
            }
          }
        }
      }
    }
  //print out the grid
    for(int y = 0; y < n; y++) {
      for(int x = 0; x < m; x++) {
        //println at the right edge of the grid
        if(x == m - 1) {
          if(mine[x][y] >= 5) {
            System.out.println("*");
          }
          else {
            System.out.println(mine[x][y]);
          }
        }
        //other tiles, no need to switch lines
        else {
          if(mine[x][y] >= 5) {
            System.out.print("*  ");
          }
          else {
            System.out.print(mine[x][y] + "  ");
          }
        }
      } 
    }
  }
}

感谢您抽出宝贵时间,如果有任何建议,我将不胜感激。

【问题讨论】:

  • 这是一个问得很好的问题,我希望我能给你一个更好的答案,但基本上你需要考虑如何让你的代码更通用并避免像 5 这样的硬编码数字。跨度>
  • 这是code review 的东西,而不是stackoverflow。请把问题移到那里。

标签: java arrays refactoring minesweeper cyclomatic-complexity


【解决方案1】:

我不会分享重构的代码,但会给你一些你应该做的想法和例子。我认为它对您的学习会更有用。

您应该找到相同的条件并将它们从几个分支中提取为一个。我建议你先从内在条件开始,然后到外面去。

我们以y == 0条件的分支为例:

if(x == 0) {
  if(mine[x + 1][y] >= 5) {
     mine[x][y] += 1;
  }
  if(mine[x][y + 1] >= 5) {
    mine[x][y] += 1;
  }
}
//upper right corner
else if(x == m - 1) {
  if(mine[x - 1][y] >= 5) {
     mine[x][y] += 1;
  }
  if(mine[x][y + 1] >= 5) {
     mine[x][y] += 1;
  } 
//mid of first row
else {
  if(mine[x - 1][y] >= 5) {
    mine[x][y] += 1;
  }
  if(mine[x + 1][y] >= 5) {
    mine[x][y] += 1;
  }
  if(mine[x][y + 1] >= 5) {
    mine[x][y] += 1;
  } 
}

您可以看到您在x == 0else 分支中检查了mine[x + 1][y] &gt;= 5。您可以将两个条件合二为一,它看起来像x &lt; m-1,现在代码看起来像:

if(x < m-1 && mine[x + 1][y] >= 5) {
  mine[x][y] += 1;
}

if(x == 0) {
  if(mine[x][y + 1] >= 5) {
    mine[x][y] += 1;
  }
}
//upper right corner
else if(x == m - 1) {
  if(mine[x - 1][y] >= 5) {
     mine[x][y] += 1;
  }
  if(mine[x][y + 1] >= 5) {
     mine[x][y] += 1;
  } 
//mid of first row
else {
  if(mine[x - 1][y] >= 5) {
    mine[x][y] += 1;
  }
  if(mine[x][y + 1] >= 5) {
    mine[x][y] += 1;
  } 
}

继续每个 if 语句,你在哪里使用x 条件。当你用x 结束时,对y 的条件执行相同的操作。从多个分支中提取公共部分。

关于打印结果的部分。请想想你应该怎么做。您应该打印 *mine[x][y] 取决于 mine[x][y]==5 并且打印空格或 new_line (System.out.println()) 取决于 x == m - 1。现在想想如何实现它。

【讨论】:

    【解决方案2】:

    如果循环进行左/右/上/下测试,可以降低复杂度,节省大量代码:

    for (int tryX = -1; tryX <= 1; tryX++) {
      for (int tryY = -1; tryY <= 1; tryY++) {
        if(mine[x + tryX][y + tryY] >= 5) {
          mine[x][y] += 1;
        }
      }
    }
    

    由于这需要很多代码行,因此会降低复杂性。 您应该使用 IDE 将代码提取到方法中(有关 IntelliJ,请参阅 here)。 好的提取点是循环。

    我看到两个不错的提取点:

    1. initArrayWithRandomMines()
    2. calculateNeighborMines()

    【讨论】:

    • 看起来x=0和y=0时会抛出java.lang.ArrayIndexOutOfBoundsException
    • 好点,你当然需要限制检查。这个想法是为了展示这个概念:-)。
    猜你喜欢
    • 2020-10-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-02-13
    • 1970-01-01
    相关资源
    最近更新 更多