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 .