我有一个 Validator
接口(interface),它提供了一个 isValid(Thing)
方法,返回一个 ValidationResult
其中包含一个 boolean
和原因消息。
我想创建此接口(interface)的 ValidatorAggregator
实现,它在多个 Validator
之间执行 OR(如果任何 Validator
返回肯定结果,则结果为正)。如果任何 validator 成功,我想短路并返回其结果。如果没有 validator 成功,我想返回所有失败消息。
我可以使用流和 findFirst().orElse(...)
简洁地完成此操作,但是如果使用此模式,如果 findFirst
返回空,我将丢失所有中间结果:
public ValidationResult isValid(final Thing thing) {
return validators.stream()
.map(v -> validator.isValid(thing))
.filter(ValidationResult::isValid)
.findFirst()
.orElseGet(() -> new ValidationResult(false, "All validators failed'));
}
有没有什么方法可以使用流捕获失败的结果,或者确实比下面的方法更简洁?
public ValidationResult isValid(final Thing thing) {
final Set<ValidationResult> failedResults = new HashSet<>();
for (Validator validator : validators) {
final ValidationResult result = validator.isValid(thing);
if (result.isValid()) {
return result;
}
failedResults.add(result);
}
return new ValidationResult(false, "No successful validator: " + failedResults);
// (assume failedResults stringifies nicely)
}
编辑:根据评论,我同意我正在尝试做的是过早的优化(特别是因为这些 validator 非常轻量级)。我可能会采用类似于 Holger 的计算所有验证并将其划分为成功/不成功结果的解决方案。
这被标记为 Can you split a stream into two streams? 的骗局partitioningBy
的答案有点像,但我认为这个问题是在问,而讨论是在回答一个不同的问题。
最佳答案
没有完美的解决方案可以以相同的效率处理所有情况。即使您的循环变体满足了短路和只处理 validator 一次的标准,也有创建和填充集合的缺点,如果只有一个验证成功,这可能是不必要的。
选择取决于与操作相关的实际成本以及至少进行一次成功验证的可能性。如果以最佳性能处理常见情况,它可能会超过解决方案对处理不常见情况的惩罚。
所以
// you may use this if the likelihood of a success is high; assumes
// reasonable costs for the validation and consists (repeatable) results
public ValidationResult isValid(final Thing thing) {
return validators.stream()
.map(v -> v.isValid(thing))
.filter(ValidationResult::isValid)
.findFirst()
.orElseGet(() -> new ValidationResult(false, "All validators failed"
+ validators.stream().map(v -> v.isValid(thing)).collect(Collectors.toSet())));
}
// you may use this if the likelihood of a success is
// very low and/or you intent to utilize parallel processing
public ValidationResult isValid(final Thing thing) {
Map<Boolean,Set<ValidationResult>> results = validators.stream()
.map(v -> v.isValid(thing))
.collect(Collectors.partitioningBy(ValidationResult::isValid, Collectors.toSet()));
return results.get(true).stream().findAny()
.orElseGet(() -> new ValidationResult(false,
"No successful validator: "+results.get(false)));
}
// if chances of successful validation are mixed or unpredictable
// or validation is so expensive that everything else doesn't matter
// stay with the loop
public ValidationResult isValid(final Thing thing) {
final Set<ValidationResult> failedResults = new HashSet<>();
for (Validator validator : validators) {
final ValidationResult result = validator.isValid(thing);
if (result.isValid()) {
return result;
}
failedResults.add(result);
}
return new ValidationResult(false, "No successful validator: " + failedResults);
}
考虑对列表进行排序,以便成功机会较高的验证者位于开头……
关于java - 返回 Java 流中的第一个结果匹配谓词或所有非匹配结果,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48997935/