2

我在理解验证库时遇到了一些麻烦,io.vavr.control.Validation . 冒着提出过于宽泛的问题的风险,我确实有几个子问题——但我相信它们密切相关,并且会拼凑起来帮助我理解使用这种验证机制的正确方法。

我从这里的示例开始:https ://softwaremill.com/javaslang-data-validation 。

Validation<String, ValidRegistrationRequest> validate(RegistrationRequest request) {
   return combine(
       validateCardId(request.getCardId()),
       validateTicketType(request.getTicketType()),
       validateGuestId(request.getGuestId())
)
       .ap(ValidRegistrationRequest::new)
       .mapError(this::errorsAsJson);
}

private Validation<String, Card> validateCardId(String cardId) {
    // validate cardId
    // if correct then return an instance of entity the cardId corresponds to
}

private Validation<String, TicketType> validateTicketType(String ticketType) {
    // validate ticketType
    // if known then return enumeration representing the ticket
}

private Validation<String, Guest> validateGuest(String guestId) {
    // validate guestId
    // if correct then return an instance of entity the questId corresponds to
}

起初,我不明白泛型参数的Validation<String, ValidRegistrationRequest>来源。我现在明白它们分别与传递给mapError和的方法的返回类型相关联ap。但:

  1. 怎么combine知道返回Validation<String, ValidRegistrationRequest>?我觉得这是可能的唯一方法,是 ifcombine实际上是 a Validation<String, ValidRegistrationRequest>::combine,所以apandmapError是从这个模板定义的。但我不相信编译器应该能够暗示这combine指的是返回类型类中的静态实现。这里发生了什么事?

  2. [次要] 使用 aValidRegistrationRequest而不是 just RegistrationRequestagain 的用例是什么?我很想在我的编码中做后者,直到我看到一个例子。

我正在阅读的第二个示例在这里:http ://www.vavr.io/vavr-docs/#_validation 。

class PersonValidator {

    private static final String VALID_NAME_CHARS = "[a-zA-Z ]";
    private static final int MIN_AGE = 0;

    public Validation<Seq<String>, Person> validatePerson(String name, int age) {
        return Validation.combine(validateName(name), validateAge(age)).ap(Person::new);
    }

    private Validation<String, String> validateName(String name) {
        return CharSeq.of(name).replaceAll(VALID_NAME_CHARS, "").transform(seq -> seq.isEmpty()
                ? Validation.valid(name)
                : Validation.invalid("Name contains invalid characters: '"
                + seq.distinct().sorted() + "'"));
    }

    private Validation<String, Integer> validateAge(int age) {
        return age < MIN_AGE
                ? Validation.invalid("Age must be at least " + MIN_AGE)
                : Validation.valid(age);
    }

}
  1. 是从哪里来Seq的?mapError当没有提供时,这是默认值吗?但我正在查看 Validation.class 的反编译 .class 文件,唯一的参考Seq是这里:

      static <E, T> Validation<List<E>, Seq<T>> sequence(Iterable<? extends Validation<List<E>, T>> values) {
        Objects.requireNonNull(values, "values is null");
        List<E> errors = List.empty();
        List<T> list = List.empty();
        Iterator var3 = values.iterator();
    
        while(var3.hasNext()) {
          Validation<List<E>, T> value = (Validation)var3.next();
          if (value.isInvalid()) {
            errors = errors.prependAll(((List)value.getError()).reverse());
          } else if (errors.isEmpty()) {
            list = list.prepend(value.get());
          }
        }
    
        return errors.isEmpty() ? valid(list.reverse()) : invalid(errors.reverse());
      }
    

    哪个,我认为不相关。也许我使用的是过时的Validation?(毕竟是javaslang.control.Validation在我的导入中,而不是io.vavr.control.Validation。)

  2. 我对这两个例子都有这个问题:如何combine知道将哪些参数传递给构造函数(ap),以及以什么顺序?答案是“所有它的参数,按给定的顺序”吗?

提前致谢。

4

2 回答 2

9

当我第一次寻找 Vavr 的验证机制时,你有同样的问题和疑问。

以下是我对前两个问题的回答:

  1. combine(...)方法返回一个验证构建器的实例,在这种情况下,这是一个包含Builder3三个validate*(...)函数结果的类。该ap(...)方法是该构建器的方法并触发Validation实例的构建。

当它被调用时,验证结果将一个一个地应用于作为参数提供的函数的柯里化版本:

v3.ap(v2.ap(v1.ap(Validation.valid(f.curried()))))

在示例中,fValidRegistrationRequest类的构造函数。最后,我们有一个包含有效请求实例的验证。

另一方面,如果任何结果无效,则该方法会创建一个带有错误消息列表的无效结果。并调用mapError(this::errorsAsJson)Validation这次是实例!)将其转换为 JSON 格式。

  1. 使用的用例是ValidRegistrationRequest什么?

我在我的一个项目中使用了 Vavr 的验证。我有一个带有一些实体标识符的请求。为了验证它的正确性,我必须查询一个数据库来检查每个 id 是否有一些东西。

因此,如果验证与原始请求一起返回,我将不得不再次从数据库中获取这些对象。因此,我决定返回ValidRegistrationRequest持有域对象。只调用一次数据库,请求处理速度明显更快。

并回答第二对问题:

  1. 是的你是对的。如果结果无效,则Validation.combine(...).ap(...)返回一个Invalid类实例,其中包含从验证方法返回的错误消息列表。

如果您查看来源和Validation.ap(...)方法,您会看到无效结果被收集到 Vavr 中List。因为它继承自,所以您可以在示例Seq中看到这种类型。validatePersonSeq<String>

  1. 对,就是这样。“它的所有参数,按照给定的顺序”:)

in 参数的顺序combine必须与提供给ap(...)方法的函数所采用的参数顺序相同。

下载源代码后,更容易跟踪 Vavr 的内部结构。

于 2018-01-26T16:21:56.253 回答
0

好的,这是我尝试回答我自己的问题,但是请有经验的人确认一下会很好。我在这里找到了 Validation 的最新来源。

示例 1

  1. 我复制示例的文章实际上说combine“静态导入以提高可读性”。我错过了。所以,我是对的——我们正在调用一个静态方法。具体来说,这个:

    static <E, T1, T2, T3> Builder3<E, T1, T2, T3> combine(Validation<E, T1> validation1, Validation<E, T2> validation2, Validation<E, T3> validation3) {
        Objects.requireNonNull(validation1, "validation1 is null");
        Objects.requireNonNull(validation2, "validation2 is null");
        Objects.requireNonNull(validation3, "validation3 is null");
        return new Builder3<>(validation1, validation2, validation3);
    }
    
  2. 我对使用的猜测ValidRegistrationRequest只是在编译时强制验证。也就是说,这样一来,RegistrationRequest如果所有消费代码都需要ValidRegistrationRequest.

示例 2

  1. 我认为Set来自这里:

    /**
     * An invalid Validation
     *
     * @param <E> type of the errors of this Validation
     * @param <T> type of the value of this Validation
     */
    final class Invalid<E, T> implements Validation<E, T>, Serializable {
    
        ...
    
        @Override
        public Seq<E> getErrors() {
            return errors;
        }
    
        ...
    }
    

    然后与此有关:

    /**
     * Applies a given {@code Validation} that encapsulates a function to this {@code Validation}'s value or combines both errors.
     *
     * @param validation a function that transforms this value (on the 'sunny path')
     * @param <U>        the new value type
     * @return a new {@code Validation} that contains a transformed value or combined errors.
     */
    @SuppressWarnings("unchecked")
    default <U> Validation<E, U> ap(Validation<E, ? extends Function<? super T, ? extends U>> validation) {
        Objects.requireNonNull(validation, "validation is null");
        if (isValid()) {
            return validation.map(f -> f.apply(get()));
        } else if (validation.isValid()) {
            return (Validation<E, U>) this;
        } else {
            return invalidAll(getErrors().prependAll(validation.getErrors()));
        }
    }
    
    1. @mchmiel 在我写我的时候回答了我的问题。
于 2018-01-26T16:24:03.013 回答