Выполнение одной и той же операции над несколькими функциями - PullRequest
3 голосов
/ 05 апреля 2019

У меня есть класс с несколькими get функциями, такими как getF1 до getF10. Я хочу для каждого из этих получателей заменить букву "x" на "a" (случайный пример). Получатель может вернуть нулевое значение.

Пока что это то, что я сделал, и это работает, есть ли способ сделать что-то лучше, чем это?

public void foo(final MyObject bar) {
    Optional.of(bar).map(MyObject::getF1).ifPresent(s -> bar.setF1(s.replaceAll("x", "a"));
    Optional.of(bar).map(MyObject::getF2).ifPresent(s -> bar.setF2(s.replaceAll("x", "a")));
    ...
    Optional.of(bar).map(MyObject::getF10).ifPresent(s -> bar.setF10(s.replaceAll("x", "a")));
}

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

public void foo(final MyObject bar) {
    List<Function> func = new ArrayList<Function>();
    func.addAll(Arrays.asList(MyObject::getF1, MyObject::getF2, ..., MyObject::getF10));
    Optional.of(bar).map(func).ifPresent(s -> func(s.replaceAll("x", "a"));
}

Может быть, работа с потоком могла бы сделать работу?

Спасибо!

Ответы [ 2 ]

3 голосов
/ 05 апреля 2019

Вы можете перебирать ваши геттеры и сеттеры как парные Supplier с и Consumer с:

public void foo(final MyObject bar) {

    if (bar == null)
        return;

    Map<Supplier<String>, Consumer<String>> funcs = new HashMap<>();
    funcs.put(bar::getF1, bar::setF1);
    funcs.put(bar::getF2, bar::setF2);

    funcs.forEach(
            (getter, setter) -> Optional.of(getter.get()).ifPresent(s -> setter.accept(s.replaceAll("x", "a"))));
}

Также обратите внимание, что обработка аргумента null с помощью Optional была заменена защитным предложением: это должно произойти до разрешения bar::..., чтобы предотвратить NPE. Это также делает понятную обработку null.

3 голосов
/ 05 апреля 2019

Вы можете хранить картографы, используемые в Optional::map, а потребители, используемые в Optional::ifPresent, в Map.

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

private String replaced(String string) {
    return string.replaceAll("x", "a");
}

Затем просто переберите записи и примените каждую из пар ключ-значение (порядок не имеет значения):

Map<Function<? super MyObject, ? extends String>, Consumer<? super String>> map = new HashMap<>();
map.put(MyObject::getF1, bar::setF1);
map.put(MyObject::getF2, bar::setF2);
map.put(MyObject::getF10, bar::setF10);
// ...

map.forEach((function, consumer) -> {
        Optional.of(bar).map(function).map(this::replaced).ifPresent(consumer);
});

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

public final class Mapping {

    private final Function<MyObject, String> getterFunction;
    private final Function<String, String> transformationFunction;
    private final Consumer<String> setterFunction;

    public Mapping(final Function<MyObject, String> getterFunction, final Function<String, String> transformationFunction,
        final Consumer<String> setterFunction) {
        this.getterFunction = getterFunction;
        this.transformationFunction = transformationFunction;
        this.setterFunction = setterFunction;
    }

    // getters
}

И использование аналогично (функции преобразования являются примерами и могут отличаться):

List<Mapping> list = new ArrayList<>();
list.add(new Mapping(MyObject::getF1, s -> s.replaceAll("x", "a"), bar::setF1));
list.add(new Mapping(MyObject::getF2, s -> s.replaceAll("x", "a"), bar::setF2));
list.add(new Mapping(MyObject::getF10, s -> s.replaceAll("x", "a"), bar::setF10));

list.forEach(mapping -> {
    Optional.of(bar)
            .map(mapping.getGtterFunction)
            .map(mapping.getTransformationFunction)
            .ifPresent(mapping.getSetterFunction);
});
...