Объедините потребителей с разными аргументами или разными аргументами - PullRequest
2 голосов
/ 24 апреля 2019

Допустим, наш метод получает input String и возвращает некоторый List output .Этот вывод является результатом некоторого количества генераторов, некоторые из которых зависят от ввода, а некоторые нет - они просто добавляют предопределенные значения.Я хочу реализовать эти генераторы как список некоторых функциональных интерфейсов (например, Consumer), затем объединить их в один Consumer и просто применить его к входной строке.

Итак, я могу легко и самостоятельно менять небольшие генераторы.Но проблема в том, что не все мои генераторы нуждаются в input String в качестве параметра, и я просто передаю этот параметр только по одной причине - у меня есть возможность объединять таких потребителей с другими.

public class ConsumersTest {

private static <T, U> BiConsumer<T, U> combine(List<BiConsumer<T, U>> consumers) {
    return consumers.stream().reduce((arg1, arg2) -> {}, BiConsumer::andThen);
}

List<String> generate(String input) {
    ArrayList<String> output = new ArrayList<>();
    combine(getGenerators()).accept(input, output);
    return output;
}

private List<BiConsumer<String, List<String>>> getGenerators() {
    return Arrays.asList(
            this::addFirstDependent,
            this::addSecondIndependent
    );
}

private void addFirstDependent(String input, List<String> output) {
    if (input.contains("some string")) {
        output.add("First-Dependent");
    }
}

private void addSecondIndependent(String input, List<String> output) {
    output.add("Predefined Output");
}}

Можно ли объединить разных потребителей под одним зонтиком и применять их в одном месте?Или это плохая идея и не правильный способ делать такие вещи?

1 Ответ

5 голосов
/ 25 апреля 2019

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

public class ConsumersTest {

    List<String> generate(String input) {
        ArrayList<String> output = new ArrayList<>();
        generators.accept(input, output);
        return output;
    }

    private static <T, U> BiConsumer<T, U> ignoreFirstArg(Consumer<U> consumer) {
        return (t, u) -> consumer.accept(u);
    }

    private final BiConsumer<String, List<String>> generators =
        Stream.<BiConsumer<String, List<String>>>of(
                this::addFirstDependent,
                ignoreFirstArg(this::addSecondIndependent)
        ).reduce(BiConsumer::andThen).orElse((arg1, arg2) -> {});

    private void addFirstDependent(String input, List<String> output) {
        if (input.contains("some string")) {
            output.add("First-Dependent");
        }
    }

    private void addSecondIndependent(List<String> output) {
        output.add("Predefined Output");
    }
}

Итак, ignoreFirstArg - это общий адаптер для методов, не имеющих этого первого параметра. Может быть произвольное количество методов адаптера. Но учтите, что если адаптер очень специфичен и, таким образом, использует только один раз, можно также написать лямбда-выражение вместо ссылки на метод прямо в коде объединения. Обратите внимание, что я изменил этот код, чтобы не выполнять повторную оценку для каждого вызова generate(String input), так как в противном случае не было бы смысла объединять их, если вы не используете повторно комбинированную функцию, поскольку вы также можете использовать

List<String> generate(String input) {
    ArrayList<String> output = new ArrayList<>();
    Stream.<BiConsumer<String, List<String>>>of(
            this::addFirstDependent,
            ignoreFirstArg(this::addSecondIndependent)
    ).forEach(g -> g.accept(input, output));
    return output;
}

или даже проще

List<String> generate(String input) {
    ArrayList<String> output = new ArrayList<>();

    this.addFirstDependent(input, output);
    this.addSecondIndependent(output);

    return output;
}

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

...