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

В условии if следующим образом:

if( condition1 || condition2 || condition3 || condition4 || condition5)

Там, где условия не зависят друг от друга, сложность кода, как правило, высока, есть ли способ реорганизовать эту логику, чтобы уменьшить сложность?

Условия здесь могут представлять методы, которые выполняют проверку и возвращают логическое значение.

Я добавляю фрагмент кода для ясности:

public void doSomething(boolean val, boolean val2, boolean val3, boolean val4, boolean val5, boolean val6)
    {
        if(val || val2 || val3|| val4|| val5|| val6)
        {
            System.out.println("hello");
        }
        else{
            System.out.println("hello world");
        }
    }

Сложность приведенного выше фрагмента равна 7.

Как я могу уменьшить его?


person venkat g    schedule 14.02.2017    source источник
comment
вы должны показать больше кода, потому что этот код не имеет цикломатической сложности: у вас есть один путь.   -  person davidxxx    schedule 14.02.2017
comment
Почему вас беспокоит цикломатическая сложность? Хотели бы вы уменьшить читабельность, если бы вы могли уменьшить цикломатическую сложность?   -  person Ole V.V.    schedule 14.02.2017
comment
Насколько я понимаю, цикломатическая сложность — это количество линейно независимых путей по коду. Простым способом сокращения было бы использование | вместо ||, так как это заставляло бы каждый раз оценивать все условия. Хотя я с трудом вижу улучшения.   -  person Ole V.V.    schedule 14.02.2017
comment
Почему у вас есть сигнатура метода, которая принимает 6 логических значений? Я бы больше заботился о читабельности и удобстве использования, чем о цикломатической сложности   -  person blank    schedule 14.02.2017
comment
@ОлеВ.В. Я предполагаю, что кого-то может волновать цикломатическая сложность, потому что какой-то инструмент, такой как Sonar, используется, он жалуется на кучу локализованных проблем и стандартов кодирования, а затем разработчики должны исправлять свой код, пока он не перестанет ворчать. Затем менеджеры могут похлопать себя по плечу за то, что они предприняли шаги по улучшению кодовой базы, даже несмотря на то, что общая картина, архитектура, все еще может представлять собой рушащийся беспорядок. Или, может быть, я просто горький.   -  person G_H    schedule 14.02.2017


Ответы (3)


Можно получить логические флаги, заполненные для каждого условия, и использовать эти флаги в операторе if. На мой взгляд, это также улучшит читаемость.

person PankajT    schedule 14.02.2017
comment
Но это не решит проблему сложности, верно? - person venkat g; 14.02.2017

В данном конкретном случае я бы сделал этот код более универсальным:

public void doSomething(boolean... val) {
    for (boolean v : val) {
        if (v) { 
           System.out.println("hello");
           return;
        }
    }

    System.out.println("hello world");
}

Это позволит вам не указывать количество аргументов в методе, если ваша логика внутри этого метода действительно такая (т.е. выполните ДЕЙСТВИЕ №1, если какой-либо из аргументов равен true, в противном случае выполните ДЕЙСТВИЕ №2).

person Andremoniy    schedule 14.02.2017

После вашего редактирования:

«Сложность приведенного выше фрагмента равна 7»

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

Если вы замените его

  if(val || val2 || val3|| val4|| val5|| val6)

by

   boolean condition = val || val2 || val3|| val4|| val5|| val6;          
   if(condition)

в чем сложность сейчас?


В разработке цикломатическая сложность обычно относится к потенциальным путям в потоке кода. Достаточно часто это связано с вложенными условными блоками.

Мы говорим о коде, который имеет важную цикломатическую сложность, как о стрелочном коде, поскольку вложенные уровни рисуют своего рода стрелку.

В вашем примере кода это не проблема, так как у вас есть только три возможных пути:

public void doSomething(boolean val, boolean val2, boolean val3, boolean val4, boolean val5, boolean val6)
    {
        if(val || val2 || val3|| val4|| val5|| val6)
        {
            System.out.println("hello");
        }
        else{
            System.out.println("hello world");
        }
    }
  • Первый путь : if(val || val2 || val3|| val4|| val5|| val6)
  • Второй путь : else{
  • Третий путь: код между else и концом метода

Чем меньше в коде возможных путей, тем легче его читать, тестировать и поддерживать.

В вашем слишком простом случае вы можете уменьшить сложность, используя не оператор else. Которые удаляют потенциальный путь:

public void doSomething(boolean val, boolean val2, boolean val3, boolean val4, boolean val5, boolean val6)
    {
        if(val || val2 || val3|| val4|| val5|| val6)
        {
            System.out.println("hello");
            return;
        }

        System.out.println("hello world");           
    }

Но очевидно, что ваши примеры слишком просты, чтобы выявить серьезные проблемы, связанные со сложностью.
Вот модифицированная версия вашего примера кода, где сложность связана с рефакторингом кода для уменьшения цикломатической сложности.

public void doSomething(boolean val, boolean val2, boolean val3, boolean val4, boolean val5, boolean val6)
    {
        if(val || val2 || val3|| val4|| val5|| val6)
        {
            if (condition){
                  if (val8 && val9){
                     ...
                  } 
                  else {
                      if (condition2 && condition3){
                         System.out.println("hello");
                      }
                  }
            }

        }
        else{
            System.out.println("hello world");
        }
    }

Как правило, чтобы уменьшить цикломатическую сложность, вы должны уменьшить количество возможных путей. Вы можете добиться этого:

  • избегая необязательных else
  • группировка операторов условий, которые разделены, в то время как это может быть один оператор, который
  • выход из метода, когда это позволяет условие, а не ожидание единственной точки выхода.

person davidxxx    schedule 14.02.2017
comment
Небольшое примечание: return; необходим в блоке if, во фрагменте без else, чтобы сохранить тот же вывод. - person Linuslabo; 14.02.2017