在Java中简化验证规则序列

inkz8wg9  于 2023-10-14  发布在  Java
关注(0)|答案(1)|浏览(95)

我有一个对象的验证列表,现在这些验证非常昂贵,并且涉及到对另一个服务的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验证库,但需要一些帮助。

ukqbszuj

ukqbszuj1#

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

相关问题