Как уменьшить цикломатическую сложность?

Спасибо, что прочитали мой вопрос. В настоящее время я изучаю Java-курс на Coursera, и меня попросили написать программу на сапере для выполнения задания. Мой код дает правильный результат, но моя оценка была сильно вычтена, потому что мой код «чрезмерно сложный, с цикломатической сложностью 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] + "  ");
          }
        }
      } 
    }
  }
}

Спасибо за ваше время, и я буду очень признателен за любые предложения.


person NonMiPiaceJava    schedule 01.03.2020    source источник
comment
Это хорошо заданный вопрос, и я хотел бы получить для вас лучший ответ, но в основном вам нужно подумать, как сделать свой код более общим и избежать жестко закодированных чисел, таких как 5.   -  person Ian Newson    schedule 01.03.2020
comment
Это что-то для проверки кода, а не для stackoverflow. Пожалуйста, перенесите вопрос туда.   -  person akuzminykh    schedule 01.03.2020


Ответы (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 ). Хорошие точки извлечения - петли.

Я вижу две хорошие точки извлечения:

  1. initArrayWithRandomMines ()
  2. CalculNeighborMines ()
person s.fuhrm    schedule 01.03.2020
comment
Похоже, будет java.lang.ArrayIndexOutOfBoundsException брошено, когда x = 0 и y = 0 - person Maxim Popov; 01.03.2020
comment
Хорошая мысль, вам, конечно же, нужна проверка лимитов. Идея заключалась в том, чтобы показать концепт :-). - person s.fuhrm; 01.03.2020

Я не буду рассказывать об отредактированном коде, но приведу несколько идей и примеров, которые вам следует сделать. Думаю, это будет более полезно для вашего обучения.

Вы должны найти одинаковые условия и извлечь их из нескольких веток в одну. Я рекомендую вам начать с внутренних условий, а затем выбраться наружу.

Возьмем для примера ветку с условием 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;
  } 
}

Вы можете видеть, что вы проверяете mine[x + 1][y] >= 5, когда x == 0 или в ветке else. Вы можете объединить два условия в одно, и это будет выглядеть как x < 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, а пробелы или новая_строка (System.out.println()) зависят от x == m - 1. А теперь подумайте, как это реализовать.

person Maxim Popov    schedule 01.03.2020