Как обобщить статическое clousure? - PullRequest
0 голосов
/ 28 апреля 2018

У меня есть следующий сценарий: два помощника проверки

StringValidationHelper ...

public class StringValidationHelper {

    public static Validation<String> notNull = 
        SimpleValidation.from(s -> s != null, "must not be null.");

    public static Validation<String> moreThan(int size) {
        return SimpleValidation.from(
            s -> s.length() >= size, 
            String.format ("must have more than %s chars.", size));
    }
        ... // More methods (lessThan, etc)}

... и NumberValidationHelper.

public class NumberValidationHelper {

    public static Validation<Number> notNull = 
        SimpleValidation.from(n -> n != null, "must not be null");

    public static <N extends Number & Comparable<N>> Validation<N> lowerThan(N max){
        return SimpleValidation.from(
            n -> n.compareTo(max) == -1,
            String.format("must be lower than %s.", max));
    }
    ... // More methods like (greaterThan, etc)}

Метод из - это статический фабричный метод, который получает предикат и сообщение о возможной проверке завершается неудачей.

public class SimpleValidation<K> implements Validation<K>{
    private Predicate<K> predicate;
    private String onErrorMessage;

    private SimpleValidation(Predicate<K> predicate, String onErrorMessage) {
        this.predicate = predicate;
        this.onErrorMessage = onErrorMessage;
    }

    public static <K> SimpleValidation<K> from(Predicate<K> predicate, String onErrorMessage){
        return new SimpleValidation<>(predicate, onErrorMessage);
    }
    ... // Omitted for simplicity
}

Благодаря интерфейсу проверки вы можете наслаждаться удивительно плавным интерфейсом

    @FunctionalInterface
    public interface Validation<K> {

        ... // Omitted for simplicity

        default Validation<K> and(Validation<K> other) {
            return param -> {
                ValidationResult firstResult = this.test (param);
                return ! firstResult.isValid()? firstResult: other.test(param);
            };
        }
        ... // Omitted for simplicity
    }

Таким образом, я могу начать, например, проверку с использованием замыкания notNull.

Пример: с NumberValidationHelper

public class MyValidate {
    void validate(int toValidate) {
        notNull.and(lowerThan(100)).test(toValidate).isValid();
    }
}

Эта структура проверки, которую я разработал на основе этой статьи.

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

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

public abstract class GenericHelper<K> {
    public static Validation<K> notNull = SimpleValidation.from(o -> o != null, "must not be null.");
}

Также меня не беспокоит ввод Validation с Object, как показано ниже:

public abstract class GenericHelper {

    public static Validation<Object> notNull = SimpleValidation.from(o -> o != null, "must not be null.");
}

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

notNull.and(lowerThan(100)).test(toValidate).isValid(); //Does not compile

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

благодарный

1 Ответ

0 голосов
/ 02 мая 2018

Вы должны ослабить общую сигнатуру and, позволяя Validation<T> с более конкретным T в качестве параметра, чтобы получить Validation<T> как результат:

default <T extends K> Validation<T> and(Validation<T> other) {
    return param -> {
        ValidationResult firstResult = this.test(param);
        return ! firstResult.isValid()? firstResult: other.test(param);
    };
}

Оставаясь со своими примерами, вы все равно не можете написать

void validate(int toValidate) {
    notNull.and(moreThan(100)).test(toValidate).isValid();
}

, так как moreThan возвращает Validation<String>, который не может test и int, но обнаружение таких ошибок - вот что такое Generics (я полагаю, у вас есть другой метод moreThan в вашей реальной кодовой базе, который Вы не включили в свой вопрос). Но теперь с вашим примером будет работать следующее:

void validate(int toValidate) {
    notNull.and(lowerThan(100)).test(toValidate).isValid();
}

Иногда вам нужно проверить проверку более определенного типа перед более общей проверкой, которая все еще не работает с методом, показанным выше. Одним из решений было бы пойти по тому же маршруту, что и разработчики JDK, и увеличить Function.andThen(after) на Function.compose(before), позволяя поменяться ролями

default <T extends K> Validation<T> compose(Validation<T> other) {
    return param -> {
        ValidationResult firstResult = other.test(param);
        return ! firstResult.isValid()? firstResult: this.test(param);
    };
}

Или вы создаете метод static, который позволяет обоим аргументам иметь более широкий тип, чем результирующий Validation:

static <T> Validation<T> and(Validation<? super T> first, Validation<? super T> second) {
    return param -> {
        ValidationResult firstResult = first.test(param);
        return ! firstResult.isValid()? firstResult: second.test(param);
    };
}

Обратите внимание, что метод static можно комбинировать с удобным методом экземпляра, поэтому вызывающему абоненту нужно прибегать к методу static только при достижении ограничений общей сигнатуры:

@FunctionalInterface
public interface Validation<K> {
    ValidationResult test(K item);

    default <T extends K> Validation<T> and(Validation<T> other) {
        return and(this, other);
    }
    static <T> Validation<T> and(Validation<? super T> first,Validation<? super T> second){
        return param -> {
            ValidationResult firstResult = first.test(param);
            return ! firstResult.isValid()? firstResult: second.test(param);
        };
    }
}

Так что вы все еще можете написать

notNull.and(lowerThan(100)).test(toValidate).isValid();

но при достижении ограничения, например

Validation<Object> anotherCriteria;
…
lowerThan(100).and(anotherCriteria).test(toValidate).isValid();

не работает, можно прибегнуть к

Validation.and(lowerThan(100), anotherCriteria).test(toValidate).isValid();
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...