Улучшение кода цикломатической сложности для операторов if-else - PullRequest
2 голосов
/ 29 мая 2019

У меня есть блок операторов if-else в методе, для которого я получаю проблему цикломатической сложности.Я попытался использовать оператор switch для этого, но проблема остается.

if (fileName.startsWith(HwErrorsEtlConstants.MR_RAW_GRADIENT_ERRORS)) {
    method1()...
} else if (fileName.startsWith(HwErrorsEtlConstants.MR_RAW_PFEI_ERRORS)) {
    method2()...
} else if (fileName.startsWith(HwErrorsEtlConstants.MR_RAW_RFAMP_ERRORS)) {
    method3()...
} and so on...

public static void method1(IOutputWriter writer, String fileName, InputStream fileInputStream) throws IOException {
        //logic for the method
    }

Ответы [ 3 ]

3 голосов
/ 29 мая 2019

Редактировать : как вы упомянули, вы проверили Exception s, которые могут быть брошены и аргументы в вашем методе. Поскольку Runnable не объявляет, что может выдать Exceptions, а также не принимает никаких параметров, вам необходимо создать свои FunctionalInterface (нажмите здесь , чтобы увидеть, что они на самом деле):

public interface ThrowingRunnable {
    void run(IOutputWriter writer, String fileName, InputStream fileInputStream) throws IOException;
}

Тогда вам просто нужно заменить Runnable на ThrowingRunnable в моем предложенном ранее коде, и у вас все будет хорошо.


Вы можете создать отображение HwErrorsEtlConstants для конкретного метода (использует Java 8):

static final Map<String, Runnable> MAPPING;
static {
    Map<String, Runnable> temp = new HashMap<>();
    temp.put(HwErrorsEtlConstants.MR_RAW_GRADIENT_ERRORS, this::method1);
    temp.put(HwErrorsEtlConstants.MR_RAW_PFEI_ERRORS, this::method2);
    temp.put(HwErrorsEtlConstants.MR_RAW_RFAMP_ERRORS, this::method3);
    MAPPING = Collections.unmodifiableMap(temp);
}

Тогда в вашем методе вы можете использовать Stream s, представленные также в Java 8:

// Optional indicates a potentially absent value, it's just a wrapper around a Runnable
Optional<Runnable> optional = MAPPING
    // create a Stream from the entries
    .entrySet().stream()
    // keep the items that match the condition and drop every other
    .filter(e -> filename.startsWith(e.getKey()))
    // we had Map.Entry<String, Runnable>, but now we only need the value e.g. the Runnable
    .map(Map.Entry::getValue)
    // short circuit, e.g. we only want the first value that matches
    .findFirst();

// checks if anything is present, this is used as the MAPPING "could" be empty
if(optional.isPresent()) {
    // unpack the value and call it with arguments
    optional.get().run(aWriter, someFileName, anInputStream);
} else {
    // nothing matched, throw error or log etc.
}

Хотя, как уже упоминалось, ваше текущее решение выглядит хорошо, я думаю, вы используете Sonar для анализа кода. Иногда у сонара просто ложные срабатывания, поэтому вы также можете спокойно их игнорировать.

Далее читаем, чтобы помочь вам понять Java 8:

2 голосов
/ 29 мая 2019

Цикломатическая сложность - это одна проблема: на самом деле циклы .

Затем происходит разделение потока на свойство с использованием if / switch, которое не является объектно-ориентированным. Это также может нарушить разделение интересов : множество методов обрабатывают совершенно разные мелкие аспекты.

Если количество полей больше, а количество строк велико, рассмотрите возможность извлечения классов, работающих с одним аспектом.

Для уменьшения цикломатической сложности установите флажок для контроля потока вызывающих абонентов, повторений одного и того же вызова, практического дублирующего кода. А затем попробуйте (пере) переместить циклы. Лучше всего, когда вы можете соединить циклы; и работа со списками / наборами / потоками.

1 голос
/ 29 мая 2019

Вы можете решить эту проблему с помощью подхода Java 8, используя функциональные интерфейсы Predicate & Function.Вы должны поместить все условия в предикаты, а в Function вы можете добавить реализацию, которая должна выполняться при совпадении условий.

Map<Predicate,Function<String,String>> map = new HashMap<>();

    Predicate<String> p = (fileName)->fileName.startsWith(HwErrorsEtlConstants.MR_RAW_GRADIENT_ERRORS);

  Function<String,String> method = (input)->{ return "output";}; 

   map.put(p,method);
    Optional<String> result =
    map.entrySet()
    .stream()
    .filter(entry->entry.getKey().test(fileName))
    .map(entry->entry.getValue().apply())
    .findFirst();

ИЛИ

  map.entrySet()
    .stream()
    .filter(entry->entry.getKey().test(fileName))
    .forEach(entry->entry.getValue().apply());
...