在 Java 中简化验证规则序列

Simplifying Validation rule sequence in Java

提问人:SAIKAT CHAKRABORTY 提问时间:10/13/2023 更新时间:10/13/2023 访问量:53

问:

我现在有一个对象的验证列表,这些验证非常昂贵,并且涉及对另一个服务的 API 调用。在调用验证服务时,我需要在第一次收到否定验证或错误/异常时短路验证链。 我目前所拥有的是每个验证帮助程序函数都会返回一个 Optional。当验证成功时,此可选项为空。如果由于调用的服务的响应或服务引发异常而导致验证失败,我们只需返回该 ValidationResult,在链的末尾,我们返回一个 Valid ValidationResult

这是此处的可选链接:

return checkBelongsToGroup(row)
                .orElseGet(() -> checkParentToGroup(row)
                        .orElseGet(() -> checkLocation(row)
                                .orElseGet(() -> builder()
                                .outcome(ValidationResult
                                        .Outcome.VALID).build())));



现在提供其中一个验证函数来体验它的外观:

public Optional<ValidationResult> checkBelongsToGroup(final Row row) {
        if (Strings.isNullOrEmpty(row.getPreviousId())) {
            return Optional.empty(); //valid
        }
        return Try.of(() -> serviceClient.getassociation(row))
                .toEither()
                .mapLeft(this::getThrowableError)
// helper function to change exception to ValidationResult
                .fold(validationError ->  Optional.of(validationError), getAssociationOutput -> {
                    final Association association = getAssociationOutput.getAssociation();
                    if (association != null && association.isActive()) {
                        return Optional.empty(); // Success case
                    } else {
                        return Optional.of(builder()
                                .outcome(ValidationResult.Outcome.INVALID)
                                .errorStringId(INVALID)
                                .build()); // Validation failed.
                    }
                });

    }


这里奇怪的部分是 Optional.empty 被用来类似于语义上错误的成功案例。有没有办法进行这种链式短路验证,直到验证的第一个异常失败。 我正在考虑 vavr 的验证库,但需要一些帮助。

Java Spring 验证 函数式编程 VAVR

评论


答:

0赞 fjavierm 10/13/2023 #1

这似乎是一个不错的主意,但我发现验证的链接有点令人困惑。在这种情况下,也许一系列典型的条件会更具可读性。像这样:Optional.empty()

var result = checkBelongsToGroup(row);

if (result.isEmpty()) {
    result = checkParentToGroup(row);
}

if (result.isEmpty()) {
    result = checkLocation(row);
}

if (!result.isEmpty()) {
    // manage error
}

// success code

另一种选择是使用像 Vavr 这样的库,或者实现类似的东西。结果看起来像(或类似,我不记得确切的语法):

Validation<ValidationResult, Void> result = Validation.combine(
    checkBelongsToGroup(row),
    checkParentToGroup(row),
    checkLocation(row)
).ap((a, b, c) -> null);

if (result.isInvalid()) {
    var failure = result.getError();
    // manage error
}

// success code

一个手动实现,没有经过测试,没有太多思考,可能看起来像:

Stream<Function<Row, ValidationResult>> validators = Stream.of(
    this::checkBelongsToGroup,
    this::checkParentToGroup,
    this::checkLocation
);

ValidationResult failure = validators
    .map(validator -> validator.apply(row))
    .filter(Optional::isPresent)
    .findFirst()
    .orElseGet(Optional::empty);

if (failure.isPresent()) {
    // manage error
}

// success code

评论

0赞 SAIKAT CHAKRABORTY 10/13/2023
对于 vavr 验证的事情,它看起来既有趣又干净。我担心的一个问题是必须保持验证的顺序,并且它应该在从一个函数(短路)返回的第一个错误/异常时返回,我不太确定这是否会发生或可以配置为 vavr 验证。