1. Обзор
Валидация-это часто встречающаяся задача в приложениях Java, и поэтому в разработку библиотек валидации было вложено много усилий.
Vavr (ранее известный как Javaslang) предоставляет полноценный API проверки|/. Это позволяет нам проверять данные простым способом, используя объектно-функциональный стиль программирования. Если вы хотите взглянуть на то, что эта библиотека предлагает из коробки, не стесняйтесь проверить эту статью .
В этом уроке мы подробно рассмотрим API проверки библиотеки и узнаем, как использовать ее наиболее релевантные методы.
2. Интерфейс Проверки
Интерфейс проверки Vavr основан на концепции функционального программирования, известной как прикладной функтор . Он выполняет последовательность функций, накапливая результаты, даже если некоторые или все эти функции выходят из строя во время цепочки выполнения.
Прикладной функтор библиотеки построен на исполнителях ее интерфейса Validation . Этот интерфейс предоставляет методы для накопления ошибок проверки и проверенных данных, что позволяет обрабатывать их как пакет.
3. Проверка Ввода Пользователем
Проверка вводимых пользователем данных (например, данных, собранных с веб-уровня) проходит гладко с помощью API проверки, поскольку она сводится к созданию пользовательского класса проверки, который проверяет данные, накапливая результирующие ошибки, если таковые имеются.
Давайте проверим имя пользователя и адрес электронной почты, которые были отправлены через форму входа в систему. Во-первых, нам нужно включить артефакт Var Maven в pom.xml файл:
io.vavr vavr 0.9.0
Далее, давайте создадим класс домена, который моделирует пользовательские объекты:
public class User {
private String name;
private String email;
// standard constructors, setters and getters, toString
}
Наконец, давайте определим наш пользовательский валидатор:
public class UserValidator {
private static final String NAME_PATTERN = ...
private static final String NAME_ERROR = ...
private static final String EMAIL_PATTERN = ...
private static final String EMAIL_ERROR = ...
public Validation, User> validateUser(
String name, String email) {
return Validation
.combine(
validateField(name, NAME_PATTERN, NAME_ERROR),
validateField(email, EMAIL_PATTERN, EMAIL_ERROR))
.ap(User::new);
}
private Validation validateField
(String field, String pattern, String error) {
return CharSeq.of(field)
.replaceAll(pattern, "")
.transform(seq -> seq.isEmpty()
? Validation.valid(field)
: Validation.invalid(error));
}
} Класс UserValidator проверяет указанное имя и адрес электронной почты индивидуально с помощью метода validateField () . В этом случае этот метод выполняет типичное сопоставление шаблонов на основе регулярных выражений.
Суть в этом примере заключается в использовании методов valid() , invalid() и combine () .
4. Методы valid(), invalid() и combine()
Если указанное имя и адрес электронной почты соответствуют заданным регулярным выражениям, метод validateField() вызывает valid() . Этот метод возвращает экземпляр Validation.Действителен . И наоборот, если значения недопустимы, метод invalid() возвращает экземпляр Validation.Недействительно .
Этот простой механизм, основанный на создании различных экземпляров Validation в зависимости от результатов проверки, должен дать нам, по крайней мере, базовое представление о том, как обрабатывать результаты (подробнее об этом в разделе 5).
Наиболее важным аспектом процесса проверки является метод combine () . Внутренне этот метод использует проверку .Класс Builder , который позволяет объединить до 8 различных экземпляров Validation , которые могут быть вычислены различными методами:
staticBuilder combine( Validation validation1, Validation validation2) { Objects.requireNonNull(validation1, "validation1 is null"); Objects.requireNonNull(validation2, "validation2 is null"); return new Builder<>(validation1, validation2); }
Самый простой Валидация.Класс Builder принимает два экземпляра проверки:
final class Builder{ private Validation v1; private Validation v2; // standard constructors public Validation , R> ap(Function2 f) { return v2.ap(v1.ap(Validation.valid(f.curried()))); } public Builder3 combine( Validation v3) { return new Builder3<>(v1, v2, v3); } }
Утверждение.Builder, вместе с методом ap(Функция) , возвращает один единственный результат с результатами проверки. Если все результаты верны, метод ap(Функция) сопоставляет результаты с одним значением. Это значение хранится в экземпляре Valid с помощью функции, указанной в его сигнатуре.
В нашем примере, если указанные имя и адрес электронной почты действительны, создается новый объект User . Конечно, можно сделать что-то совершенно другое с действительным результатом, то есть занести его в базу данных, отправить по электронной почте и так далее.
5. Обработка Результатов Валидации
Довольно легко реализовать различные механизмы обработки результатов проверки. Но как мы в первую очередь проверяем данные? В этой степени мы используем класс UserValidator :
UserValidator userValidator = new UserValidator(); Validation, User> validation = userValidator .validateUser("John", "[email protected]");
После получения экземпляра Validation мы можем использовать гибкость API проверки и обрабатывать результаты несколькими способами.
Давайте подробнее остановимся на наиболее часто встречающихся подходах.
5.1. Действительные и Недействительные экземпляры
Этот подход на сегодняшний день является самым простым. Он состоит из проверки результатов проверки с помощью экземпляров Valid и Invalid :
@Test
public void
givenInvalidUserParams_whenValidated_thenInvalidInstance() {
assertThat(
userValidator.validateUser(" ", "no-email"),
instanceOf(Invalid.class));
}
@Test
public void
givenValidUserParams_whenValidated_thenValidInstance() {
assertThat(
userValidator.validateUser("John", "[email protected]"),
instanceOf(Valid.class));
}Вместо того, чтобы проверять достоверность результатов с помощью экземпляров Valid и Invalid , мы должны просто сделать еще один шаг вперед и использовать методы isValid() и IsInvalid () .
5.2. Недопустимые() и недействительные() API
Использование тандема допустимо() / недопустимо() аналогично предыдущему подходу, с той разницей , что эти методы возвращают true или false , в зависимости от результатов проверки:
@Test
public void
givenInvalidUserParams_whenValidated_thenIsInvalidIsTrue() {
assertTrue(userValidator
.validateUser("John", "no-email")
.isInvalid());
}
@Test
public void
givenValidUserParams_whenValidated_thenIsValidMethodIsTrue() {
assertTrue(userValidator
.validateUser("John", "[email protected]")
.isValid());
}Экземпляр Invalid содержит все ошибки проверки. Их можно извлечь с помощью метода getError() :
@Test
public void
givenInValidUserParams_withGetErrorMethod_thenGetErrorMessages() {
assertEquals(
"Name contains invalid characters, Email must be a well-formed email address",
userValidator.validateUser("John", "no-email")
.getError()
.intersperse(", ")
.fold("", String::concat));
}И наоборот, если результаты верны, экземпляр User можно захватить с помощью метода get() :
@Test
public void
givenValidUserParams_withGetMethod_thenGetUserInstance() {
assertThat(userValidator.validateUser("John", "[email protected]")
.get(), instanceOf(User.class));
}Этот подход работает, как и ожидалось, но код по-прежнему выглядит довольно многословным и длинным. Мы можем сжать его дальше, используя метод в любой () .
5.3. API to Either()
Метод to Either() конструирует Left и Right экземпляры интерфейса Either . Этот дополнительный интерфейс имеет несколько удобных методов, которые могут быть использованы для сокращения обработки результатов проверки.
Если результаты действительны, результат сохраняется в экземпляре Right . В нашем примере это будет равносильно допустимому объекту User . И наоборот, если результаты неверны, ошибки сохраняются в экземпляре Left :
@Test
public void
givenValidUserParams_withtoEitherMethod_thenRightInstance() {
assertThat(userValidator.validateUser("John", "[email protected]")
.toEither(), instanceOf(Right.class));
}Теперь код выглядит гораздо более лаконичным и обтекаемым. Но мы еще не закончили. Интерфейс Validation предоставляет метод fold () , который применяет пользовательскую функцию, которая применяется к допустимым результатам, а другая-к недопустимым.
5.4. API fold()
Давайте посмотрим, как использовать метод fold() для обработки результатов проверки:
@Test
public void
givenValidUserParams_withFoldMethod_thenEqualstoParamsLength() {
assertEquals(2, (int) userValidator.validateUser(" ", " ")
.fold(Seq::length, User::hashCode));
}Использование fold() сокращает обработку результатов проверки до одной строки.
Стоит подчеркнуть, что типы возвращаемых функций, передаваемые в качестве аргументов методу, должны быть одинаковыми. Кроме того, функции должны поддерживаться параметрами типа, определенными в классе проверки, т. е. Seq и User .
6. Заключение
В этой статье мы подробно изучили API проверки Vavr и узнали, как использовать некоторые из его наиболее релевантных методов. Для получения полного списка проверьте official docs API .
Контроль проверки Vavr обеспечивает очень привлекательную альтернативу более традиционным реализациям проверки Java Beans, таким как Hibernate Validator .
Как обычно, все примеры, приведенные в статье, доступны на GitHub .