Уменьшить Cognitive Complexity метода. рефакторинг.
От: HAXT  
Дата: 13.09.18 15:56
Оценка:
Коллеги, подскажите пожалуйста способы, которыми можно уменьшить когнитивную сложность метода, средствами Java 8
Хотелось бы какое то элегантное решение, может быть предикатами или лямбдами переделать. Есть какие идеи ?


    private void fillCvsColumnsValues(ExportOrderRequestDto request, ZoneId zoneId, DateTimeFormatter dateFormat,
                                      CsvBuilder csvBuilder, PageDto<IndexedDraftOrderDto> searchOrders) {
        for (IndexedDraftOrderDto o : searchOrders.getContent()) {

            if (request.getColumns().contains(ExportColumnEnum.ORDER_CODE)) {
                csvBuilder.addNextColumnValue(o.getOrderCode());
            }

            if (request.getColumns().contains(ExportColumnEnum.STATUS)) {
                csvBuilder.addNextColumnValue(isNull(o.getOrderState()) ? "" : ORDER_STATE_DESC.get(o.getOrderState()));
            }

            if (request.getColumns().contains(ExportColumnEnum.CUSTOMER)) {
                String name = nonNull(o.getCustomer()) ? o.getCustomer().getName() : "";
                csvBuilder.addNextColumnValue(name);
            }

            if (request.getColumns().contains(ExportColumnEnum.ANIMAL_COUNT)) {
                csvBuilder.addNextColumnValue(o.getNumberOfAnimals());
            }

            if (request.getColumns().contains(ExportColumnEnum.SAVED)) {
                csvBuilder.addNextColumnValue(formatDate(o.getSavedDate(), zoneId, dateFormat));
            }

            if (request.getColumns().contains(ExportColumnEnum.PRODUCT)) {
                List<String> products = Optional.ofNullable(o.getProducts()).orElse(Collections.emptyList()).stream()
                                                .map(IndexedProductDto::getProductName)
                                                .collect(Collectors.toList());
                csvBuilder.addNextColumnValue(products);
            }

            if (request.getColumns().contains(ExportColumnEnum.MARKET)) {
                csvBuilder.addNextColumnValue(o.getMarket());
            }

            csvBuilder.endRow();
        }
Отредактировано 13.09.2018 15:58 HAXT . Предыдущая версия . Еще …
Отредактировано 13.09.2018 15:57 HAXT . Предыдущая версия .
Re: Уменьшить Cognitive Complexity метода. рефакторинг.
От: · Великобритания  
Дата: 13.09.18 16:45
Оценка: 3 (1) +1
Здравствуйте, HAXT, Вы писали:

HAX>Коллеги, подскажите пожалуйста способы, которыми можно уменьшить когнитивную сложность метода, средствами Java 8

HAX>Хотелось бы какое то элегантное решение, может быть предикатами или лямбдами переделать. Есть какие идеи ?
Зачем тут лямбды?
Вынести request.getColumns() в локальную переменную наружу цикла.
Тело цикла вынести в отдельный метод.
Строчку формирования List<String> products вынести в отдельный метод.
formatDate вынести в отедьный сервис и туда заинжектить зависимости zoneId/dateFormat, чтобы не приходилось повсюду отдельно протаскивать эти параметры.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re: Уменьшить Cognitive Complexity метода. рефакторинг.
От: elmal  
Дата: 13.09.18 18:37
Оценка: 3 (1) +2
Здравствуйте, HAXT, Вы писали:

HAX>Коллеги, подскажите пожалуйста способы, которыми можно уменьшить когнитивную сложность метода, средствами Java 8

HAX>Хотелось бы какое то элегантное решение, может быть предикатами или лямбдами переделать. Есть какие идеи ?
Сильно лучше тут не сделать.
Но:
Я бы сделал extract method того, что внутри цикла. И этот метод бы принимал columns и IndexedDraftOrderDto, а возвращал набор строк, которые добавляются в csvBuilder. Было бы немножко почище, основное мясо бы уже имело поменьше зависимостей и было бы покомпактней. То, что внутри if, возможно бы тоже выделил в методы отдельного класса extractors или convertors, скорее всего статические. И к ним комментарии почему именно так и далее на эти экстракторы можно отдельно тесты писать.

И дополнительно — мне не очень понравилось черти сколько аргументов метода, я бы постарался рефакторнуть даже уровнем выше.

Соответственно в соответствии с SRP выделил бы отдельно:
1) extractor или трансформаторы для каждого варианта столбца;
2) трансформатор для строки целиком
3) трансформатор для всех строк целиком в удобное для преобразования в csv представление. И далее это представление можно реюзать для записи не в CSV, в в другую таблицу, в excel, в Json, в xml — куда угодно. Хоть это сейчас и не надо — но может быть полезно.
4) собственно генератор csv

Но это ИМХО, именно мой вкус — я очень не люблю логику внутри for и когда что то нетривиальное внутри if. Начитался в свое время Макконела на свою голову и выработал привычку, делаю такое на автомате даже не задумываясь. А сам метод не такой уж и монструозный чтоб сильно заморачиваться чем то еще.
Re: Re: Уменьшить Cognitive Complexity метода. рефакторинг.
От: halo Украина  
Дата: 13.09.18 21:40
Оценка: 3 (1) +1
Здравствуйте, HAXT, Вы писали:

HAX>Коллеги, подскажите пожалуйста способы, которыми можно уменьшить когнитивную сложность метода, средствами Java 8


Оборачивать всё это дело в лямбды, думаю, не всегда удачная идея. Помимо накладных расходов на конструирование лямбд и их вызов, теряется гибкость в использовании операторов типа if/else, for и т.д. В своих проектах иногда использую следующую конструкцию:

  Скрытый текст
final class Builder<T>
        implements Supplier<T> {

    private T value;

    private Builder(final T value) {
        this.value = value;
    }

    static <T> Builder<T> of(final T value) {
        return new Builder<>(value);
    }

    Builder<T> run(final Runnable runnable) {
        runnable.run();
        return this;
    }

    Builder<T> run(final boolean flag, final Runnable runnable) {
        if ( flag ) {
            runnable.run();
        }
        return this;
    }

    Builder<T> run(final BooleanSupplier booleanSupplier, final Runnable runnable) {
        if ( booleanSupplier.getAsBoolean() ) {
            runnable.run();
        }
        return this;
    }

    Builder<T> run(final Predicate<? super T> predicate, final Runnable runnable) {
        if ( predicate.test(value) ) {
            runnable.run();
        }
        return this;
    }

    Builder<T> accept(final Consumer<? super T> consumer) {
        consumer.accept(value);
        return this;
    }

    Builder<T> accept(final boolean flag, final Consumer<? super T> consumer) {
        if ( flag ) {
            consumer.accept(value);
        }
        return this;
    }

    Builder<T> accept(final BooleanSupplier booleanSupplier, final Consumer<? super T> consumer) {
        if ( booleanSupplier.getAsBoolean() ) {
            consumer.accept(value);
        }
        return this;
    }

    Builder<T> accept(final Predicate<? super T> predicate, final Consumer<? super T> consumer) {
        if ( predicate.test(value) ) {
            consumer.accept(value);
        }
        return this;
    }

    @SuppressWarnings("unchecked")
    <R> Builder<R> apply(final Function<? super T, ? extends R> function) {
        value = (T) function.apply(value);
        return (Builder<R>) this;
    }

    @SuppressWarnings("unchecked")
    <R> Builder<R> apply(final boolean flag, final Function<? super T, ? extends R> function) {
        if ( flag ) {
            value = (T) function.apply(value);
        }
        return (Builder<R>) this;
    }

    @SuppressWarnings("unchecked")
    <R> Builder<R> apply(final BooleanSupplier booleanSupplier, final Function<? super T, ? extends R> function) {
        if ( booleanSupplier.getAsBoolean() ) {
            value = (T) function.apply(value);
        }
        return (Builder<R>) this;
    }

    @SuppressWarnings("unchecked")
    <R> Builder<R> apply(final Predicate<? super T> predicate, final Function<? super T, ? extends R> function) {
        if ( predicate.test(value) ) {
            value = (T) function.apply(value);
        }
        return (Builder<R>) this;
    }

    @Override
    public T get() {
        return value;
    }

}


Пример использования:

final String s = Builder.of(new StringBuilder())
        .accept(configuration::hasFoo, sb -> sb.append("foo"))
        .accept(configuration::hasBar, sb -> sb.append("bar"))
        .accept(configuration::hasBaz, sb -> sb.append("baz"))
        .apply(Object::toString)
        .get();
System.out.println(s);


То есть, если в некой конфигурации установлены флаги "foo" и "bar", но не "baz", вывод будет следующим:

foobar


В вашем случае это будет что-то типа:

Builder.of(csvBuilder)
        .run(columns.contains(ExportColumnEnum.ORDER_CODE), () -> csvBuilder.addNextColumnValue(o.getOrderCode()))
        .run(columns.contains(ExportColumnEnum.STATUS), () -> csvBuilder.addNextColumnValue(Objects.isNull(o.getOrderState()) ? "" : ORDER_STATE_DESC.get(o.getOrderState())))
        .run(columns.contains(ExportColumnEnum.CUSTOMER), () -> csvBuilder.addNextColumnValue(Objects.nonNull(o.getCustomer()) ? o.getCustomer().getName() : ""))
        .run(columns.contains(ExportColumnEnum.ANIMAL_COUNT), () -> csvBuilder.addNextColumnValue(o.getNumberOfAnimals()))
        .run(columns.contains(ExportColumnEnum.SAVED), () -> csvBuilder.addNextColumnValue(formatDate(o.getSavedDate(), zoneId, dateFormat)))
        .run(columns.contains(ExportColumnEnum.PRODUCT), () -> {
            final List<String> products = Optional.ofNullable(o.getProducts())
                    .orElse(Collections.emptyList())
                    .stream()
                    .map(IndexedProductDto::getProductName)
                    .collect(Collectors.toList());
            csvBuilder.addNextColumnValue(products);
        })
        .run(columns.contains(ExportColumnEnum.MARKET), () -> csvBuilder.addNextColumnValue(o.getMarket()))
        .get()
        .endRow();


Мне кажется, что в этом случае такой подход себя не очень оправдывает.
Re[2]: Re: Уменьшить Cognitive Complexity метода. рефакторинг.
От: · Великобритания  
Дата: 13.09.18 22:02
Оценка:
Здравствуйте, halo, Вы писали:
h> Пример использования:

h>
h> final String s = Builder.of(new StringBuilder())
h>         .accept(configuration::hasFoo, sb -> sb.append("foo"))
h>         .accept(configuration::hasBar, sb -> sb.append("bar"))
h>         .accept(configuration::hasBaz, sb -> sb.append("baz"))
h>         .apply(Object::toString)
h>         .get();
h> System.out.println(s);
h>

Мде. А чем это хуже?
final StringBuilder sb = new StringBuilder();
if(configuration.hasFoo()) sb.append("foo");
if(configuration.hasBar()) sb.append("bar");
if(configuration.hasBaz()) sb.append("baz");
final String s = sb.toString();
System.out.println(s);
avalon/2.0.6
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[2]: Уменьшить Cognitive Complexity метода. рефакторинг.
От: HAXT  
Дата: 13.09.18 22:36
Оценка:
Большое всем спасибо. Есть над чем поразмыслить !
Re[2]: Уменьшить Cognitive Complexity метода. рефакторинг.
От: HAXT  
Дата: 13.09.18 22:43
Оценка:
E>Но это ИМХО, именно мой вкус — я очень не люблю логику внутри for и когда что то нетривиальное внутри if. Начитался в свое время Макконела на свою голову и выработал привычку, делаю такое на автомате даже не задумываясь. А сам метод не такой уж и монструозный чтоб сильно заморачиваться чем то еще.

Спасибо ! И книжку "Совершенный код." Макконела куплю и изучу !
Re: Уменьшить Cognitive Complexity метода. рефакторинг.
От: bzig  
Дата: 14.09.18 02:45
Оценка: +2
enum сам по себе прекрасно подходит для хранения экстракторов

public enum ExportColumnEnum {

    ORDER_CODE(IndexedDraftOrderDto::getOrderCode),
    STATUS(o -> isNull(o.getOrderState()) ? "" : ORDER_STATE_DESC.get(o.getOrderState())),
    ...
    
    private final    Function<IndexedDraftOrderDto, Object> extractor;
    ExportColumnEnum (Function<IndexedDraftOrderDto, Object> extractor) {this.extractor = extractor);

    public Object extract(IndexedDraftOrderDto o) { return extractor.apply(o); }
}

// если порядок колонок в request.getColumns() правильный
List<ExportColumnEnum> columns = request.getColumns();
for (IndexedDraftOrderDto o : searchOrders.getContent()) {
    columns.stream().map(c -> c.extract(o)).forEach(csvBuilder::addNextColumnValue);
    csvBuilder.endRow();
}

// если порядок колонок в request.getColumns() неправильный
List<ExportColumnEnum> allColumns = Arrays.asList(ExportColumnEnum.values());
List<ExportColumnEnum> columns = request.getColumns();
for (IndexedDraftOrderDto o : searchOrders.getContent()) {
    allColumns.stream().filter(columns::contains).map(c -> c.extract(o)).forEach(csvBuilder::addNextColumnValue);
    csvBuilder.endRow();
}
Re: Уменьшить Cognitive Complexity метода. рефакторинг.
От: Artem Korneev США https://www.linkedin.com/in/artemkorneev/
Дата: 14.09.18 05:04
Оценка: 3 (1) +1
Здравствуйте, HAXT, Вы писали:

HAX>Хотелось бы какое то элегантное решение, может быть предикатами или лямбдами переделать. Есть какие идеи ?


Ну сходу пока вот что-то такое получилось.
zoneId и dataFormat воткнул как есть — я не знаю, откуда оно приходит, но их нужно будет отправить туда, где определяются правила распаковки данных — в инициализатор dataSources.
Я только с колонкой PRODUCT не очень понял. Там значение — строка или список строк? В остальных местах вроде явно строка идет, а тут коллектор возвращает список. Если и то и другое нужно, то можно сделать как bzig — Object туда поставить.

List<String> getProduct(IndexedDraftOrderDto o) {
    return Optional.ofNullable(o.getProducts()).orElse(Collections.emptyList()).stream()
        .map(IndexedProductDto::getProductName)
        .collect(Collectors.toList());
}

Map<String, Function<IndexedDraftOrderDto, String>> dataSources = new HashMap<> {{
    put(ExportColumnEnum.ORDER_CODE,   (o) -> o.getOrderCode());
    put(ExportColumnEnum.STATUS,       (o) -> isNull(o.getOrderState()) ? "" : ORDER_STATE_DESC.get(o.getOrderState()));
    put(ExportColumnEnum.CUSTOMER,     (o) -> nonNull(o.getCustomer()) ? o.getCustomer().getName() : "");
    put(ExportColumnEnum.ANIMAL_COUNT, (o) -> o.getNumberOfAnimals());
    put(ExportColumnEnum.SAVED,        (o) -> formatDate(o.getSavedDate(), zoneId, dateFormat));    
    put(ExportColumnEnum.PRODUCT,      (o) -> getProduct(o));
    put(ExportColumnEnum.MARKET,       (o) -> o.getMarket());
}}

private void fillCvsColumnsValues(ExportOrderRequestDto request, CsvBuilder csvBuilder, PageDto<IndexedDraftOrderDto> searchOrders) {
    Collection<String> columns = request.getColumns();
    for (IndexedDraftOrderDto o: searchOrders.getContent()) {
        dataSources.forEach((column, extractor) -> {
            if (columns.contains(column)) {
                csvBuilder.addNextColumnValue(extractor.apply(o));
            }
        }
    }

    csvBuilder.endRow();
}
С уважением, Artem Korneev.
Отредактировано 14.09.2018 5:32 Artem Korneev . Предыдущая версия . Еще …
Отредактировано 14.09.2018 5:31 Artem Korneev . Предыдущая версия .
Отредактировано 14.09.2018 5:12 Artem Korneev . Предыдущая версия .
Отредактировано 14.09.2018 5:12 Artem Korneev . Предыдущая версия .
Отредактировано 14.09.2018 5:09 Artem Korneev . Предыдущая версия .
Отредактировано 14.09.2018 5:09 Artem Korneev . Предыдущая версия .
Отредактировано 14.09.2018 5:06 Artem Korneev . Предыдущая версия .
Re[3]: Re: Уменьшить Cognitive Complexity метода. рефакторинг.
От: halo Украина  
Дата: 14.09.18 09:51
Оценка:
Здравствуйте, ·, Вы писали:

.> Мде. А чем это хуже?


.> if(configuration.hasFoo()) sb.append("foo");

.> if(configuration.hasBar()) sb.append("bar");
.> if(configuration.hasBaz()) sb.append("baz");

В принципе, ничем. Правда, мой форматтер перенесёт тело if на следующую строку и добавит фигурные скобки (костыли @formatter:off/@formatter:on не рассматриваю). Пример со StringBuilder, возможно, не слишком удачен, так как сам StringBuilder меняет своё внутренее состояние. Но могут возникать случаи, когда каждый вызов возвращает новое значение, например, декорируемые объекты или деревья выражений:

final IWhatever whatever = Builder.of(Whatever.create())
        .apply(configuration::hasFoo, Foo::from)
        .apply(configuration::hasBar, Bar::from)
        .apply(configuration::hasBaz, Baz::from)
        .get();
System.out.println(whatever.getClass().getSimpleName());


Для вышеупомянутой конфигурации на вывод попадёт Bar. Кроме того, можно обойтись без переприсваивания и инициализировать whatever единожды. Плюс, последее вообще может использоваться там, где операторы запрещены (делегирование в конструкторе на this или super без привлечения дополнительных методов или инициализируемые поля).

Такая идея приходила в голову не раз. В своё время работал на node.js с RethinkDB, основой запросов которого является конструирование деревьев выражений ReQL. Очень удобной в использовании получилась динамическая обёртка, опционально генерирующая новое выражение, в зависимости от входных параметров. Например:

let query = r.table('users')
    .filter({ status: 'active' });
if ( sliced ) {
    query = query.slice(begin, end);
}
if ( ordered ) {
    query = query.orderBy('name');
}
return query.run(connection);


превращалось в:

const query = maybify(r.table('users'))
    .filter({ status: 'active' })
    .maybeSlice(() => sliced, begin, end)
    .maybeOrderBy(() => ordered, 'name');
return query.run(connection);
Re[2]: Уменьшить Cognitive Complexity метода. рефакторинг.
От: HAXT  
Дата: 14.09.18 10:42
Оценка:
Здравствуйте, Artem Korneev, Вы писали:

Спасибо Артём,

Product то нормально в саму мапу встраивается а вот zoneId, dateFormat передаются с верхнего вызова и если dateFormat можно заинжектить в этом классе, то zoneId передаётся из UI через контроллер, и тут проблемы с объявлением мапы.

    private static Map<ExportColumnEnum, Function<IndexedDraftOrderDto, Object>> dataSources = ImmutableMap.of(
        ExportColumnEnum.ORDER_CODE,    (o) -> o.getOrderCode(),
        ExportColumnEnum.CUSTOMER,      (o) -> nonNull(o.getCustomer()) ? o.getCustomer().getName() : "",
        ExportColumnEnum.ANIMAL_COUNT,  (o) -> o.getNumberOfAnimals(),
        ExportColumnEnum.SAVED,         (o) -> formatDate(o.getSavedDate(), zoneId, dateFormat),
        ExportColumnEnum.PRODUCT,       (o) -> Optional.ofNullable(o.getProducts()).orElse(Collections.emptyList()).stream()
                                                    .map(IndexedProductDto::getProductName)
                                                    .collect(Collectors.toList()),
        ExportColumnEnum.MARKET,        (o) -> o.getMarket()
    );
Отредактировано 14.09.2018 10:45 HAXT . Предыдущая версия . Еще …
Отредактировано 14.09.2018 10:44 HAXT . Предыдущая версия .
Re[3]: Уменьшить Cognitive Complexity метода. рефакторинг.
От: bzig  
Дата: 14.09.18 13:25
Оценка:
Здравствуйте, HAXT, Вы писали:

HAX>Здравствуйте, Artem Korneev, Вы писали:


HAX>Спасибо Артём,


HAX>Product то нормально в саму мапу встраивается а вот zoneId, dateFormat передаются с верхнего вызова и если dateFormat можно заинжектить в этом классе, то zoneId передаётся из UI через контроллер, и тут проблемы с объявлением мапы.


Если цель отформатировать даты под юзера, то это должно быть настройкой отчета (csvBuilder) , который перехватывает даты через перегрузку метода addNextValue и форматирует.

Если цель форматировать конкретное поле в конкретной зоне, то это должно быть частью экстрактора этого поля (захардкодено)

В самом крайнем случае можно передавать Map c постпроцессорами для отдельных полей и проверять
Отредактировано 14.09.2018 13:26 мамут ушёл, и я пойду . Предыдущая версия .
Re[3]: Уменьшить Cognitive Complexity метода. рефакторинг.
От: Artem Korneev США https://www.linkedin.com/in/artemkorneev/
Дата: 14.09.18 16:04
Оценка:
Здравствуйте, HAXT, Вы писали:

HAX>Product то нормально в саму мапу встраивается а вот zoneId, dateFormat передаются с верхнего вызова и если dateFormat можно заинжектить в этом классе, то zoneId передаётся из UI через контроллер, и тут проблемы с объявлением мапы.


Ну их можно передать при создании экстракторов:

Map<String, Function<IndexedDraftOrderDto, String>> getDataSources(String zoneId, String dateFormat) {
 return new HashMap<> {{
    ...
    put(ExportColumnEnum.SAVED,        (o) -> formatDate(o.getSavedDate(), zoneId, dateFormat));    
}}

private void fillCvsColumnsValues(ExportOrderRequestDto request, CsvBuilder csvBuilder, PageDto<IndexedDraftOrderDto> searchOrders, String zoneId, String dateFormat) {
    Map<String, Function<IndexedDraftOrderDto, String>> dataSources = getDataSources(zoneId, dateFormat);
    ....
}
С уважением, Artem Korneev.
Re[4]: Уменьшить Cognitive Complexity метода. рефакторинг.
От: Слава  
Дата: 14.09.18 18:14
Оценка:
Здравствуйте, Artem Korneev, Вы писали:

AK>Ну их можно передать при создании экстракторов:


Я бы не сказал, что все эти манипуляции с Map<String, Function<IndexedDraftOrderDto, String>> прямо-таки уменьшили Cognitive Complexity. Скорее уж — увеличили. Был код простой, стал затейливый.
Re[5]: Уменьшить Cognitive Complexity метода. рефакторинг.
От: koenig  
Дата: 14.09.18 18:25
Оценка:
AK>>Ну их можно передать при создании экстракторов:

С>Я бы не сказал, что все эти манипуляции с Map<String, Function<IndexedDraftOrderDto, String>> прямо-таки уменьшили Cognitive Complexity. Скорее уж — увеличили. Был код простой, стал затейливый.


соглашусь на 100%, при том что сам не упускаю возможности такое городить
Re[5]: Уменьшить Cognitive Complexity метода. рефакторинг.
От: Artem Korneev США https://www.linkedin.com/in/artemkorneev/
Дата: 14.09.18 18:44
Оценка:
Здравствуйте, Слава, Вы писали:

С>Я бы не сказал, что все эти манипуляции с Map<String, Function<IndexedDraftOrderDto, String>> прямо-таки уменьшили Cognitive Complexity. Скорее уж — увеличили. Был код простой, стал затейливый.


Это все-таки субъективно. Мне декларативный код читать проще и быстрее. И тот и другой вариант сравнительно просты. Но исходный код линейно усложняется с увеличением количества колонок.

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

    private void fillCvsColumnsValues(ExportOrderRequestDto request, ZoneId zoneId, DateTimeFormatter dateFormat,
                                      CsvBuilder csvBuilder, PageDto<IndexedDraftOrderDto> searchOrders) {
        for (IndexedDraftOrderDto o : searchOrders.getContent()) {

            if (request.getColumns().contains(ExportColumnEnum.ORDER_CODE)) {
                csvBuilder.addNextColumnValue(o.getOrderCode());
            }

            if (request.getColumns().contains(ExportColumnEnum.MARKET)) {
                csvBuilder.addNextColumnValue(o.getNumberOfAnimals());
            }

            if (request.getColumns().contains(ExportColumnEnum.STATUS)) {
                csvBuilder.addNextColumnValue(isNull(o.getOrderState()) ? "" : ORDER_STATE_DESC.get(o.getOrderState()));
            }

            if (request.getColumns().contains(ExportColumnEnum.CUSTOMER)) {
                String name = nonNull(o.getCustomer()) ? o.getCustomer().getName() : "";
                csvBuilder.addNextColumnValue(name);
            }

            if (request.getColumns().contains(ExportColumnEnum.ANIMAL_COUNT)) {
                csvBuilder.addNextColumnValue(o.getNumberOfAnimals());
            }

            if (request.getColumns().contains(ExportColumnEnum.SAVED)) {
                csvBuilder.addNextColumnValue(formatDate(o.getSavedDate(), zoneId, dateFormat));
            }

            if (request.getColumns().contains(ExportColumnEnum.PRODUCT)) {
                List<String> products = Optional.ofNullable(o.getProducts()).orElse(Collections.emptyList()).stream()
                                                .map(IndexedProductDto::getProductName)
                                                .collect(Collectors.toList());
                csvBuilder.addNextColumnValue(products);
            }

            if (request.getColumns().contains(ExportColumnEnum.MARKET)) {
                csvBuilder.addNextColumnValue(o.getMarket());
            }

            csvBuilder.endRow();
        }


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

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

    put(ExportColumnEnum.ANIMAL_COUNT, (o) -> o.getNumberOfAnimals());
    put(ExportColumnEnum.SAVED,        (o) -> formatDate(o.getSavedDate(), zoneId, dateFormat));    
    put(ExportColumnEnum.PRODUCT,      (o) -> getProduct(o));
    put(ExportColumnEnum.MARKET,       (o) -> o.getNumberOfAnimals());


Я очень часто вижу ошибки, сделанные при копи-пасте. Декларативные подходы позволяют значительно уменьшить копи-паст и вероятность таких ошибок.
С уважением, Artem Korneev.
Отредактировано 14.09.2018 18:49 Artem Korneev . Предыдущая версия . Еще …
Отредактировано 14.09.2018 18:48 Artem Korneev . Предыдущая версия .
Отредактировано 14.09.2018 18:47 Artem Korneev . Предыдущая версия .
Отредактировано 14.09.2018 18:46 Artem Korneev . Предыдущая версия .
Re[6]: Уменьшить Cognitive Complexity метода. рефакторинг.
От: Слава  
Дата: 14.09.18 18:48
Оценка: +2
Здравствуйте, Artem Korneev, Вы писали:

AK>В варианте с лямбдами Map просто не даст добавить один элемент дважды в инициализаторе.


С этим однозначно согласен. Хорошо бы ещё эту проверку вообще на compile-time вынести.
Re[7]: Уменьшить Cognitive Complexity метода. рефакторинг.
От: Artem Korneev США https://www.linkedin.com/in/artemkorneev/
Дата: 14.09.18 18:58
Оценка:
Здравствуйте, Слава, Вы писали:

AK>>В варианте с лямбдами Map просто не даст добавить один элемент дважды в инициализаторе.

С>С этим однозначно согласен. Хорошо бы ещё эту проверку вообще на compile-time вынести.

Да, было бы хорошо. Но как минимум — IDE подсветит:



Есть подозрение, что статические анализаторы типа FindBugs тоже предупредят.
С уважением, Artem Korneev.
Re[7]: Уменьшить Cognitive Complexity метода. рефакторинг.
От: · Великобритания  
Дата: 14.09.18 19:37
Оценка: +1
Здравствуйте, Слава, Вы писали:

С> AK>В варианте с лямбдами Map просто не даст добавить один элемент дважды в инициализаторе.


С> С этим однозначно согласен. Хорошо бы ещё эту проверку вообще на compile-time вынести.

Так элементарно же:
switch(col)
{
  case ExportColumnEnum.ANIMAL_COUNT:
    return o.getNumberOfAnimals();
  case ExportColumnEnum.SAVED:
    return formatDate(o.getSavedDate(), zoneId, dateFormat);
  case ExportColumnEnum.PRODUCT:
    return getProduct(o);
  case ExportColumnEnum.MARKET:
    return o.getNumberOfAnimals();
}
avalon/2.0.6
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[2]: Просили же уменьшить complexity, а не увеличить (-)
От: Аноним931 Германия  
Дата: 17.09.18 07:01
Оценка:
"Больше 100кмч можно ехать на автобане в любом ряду кроме правого крайнего" (c) pik
"В германии земля в частной собственности" (c) pik
"Закрывать школы, при нулевой смертности среди детей и подростков, это верх глупости" (c) Abalak
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.