Java 返回可选值,具体取决于流内部使用的方法引发的异常

Java 返回可选值,具体取决于流内部使用的方法引发的异常,java,exception,code-cleanup,Java,Exception,Code Cleanup,我正在尝试实现用于处理事件的验证模块。验证模块基于简单的界面: public interface Validator { Optional<ValidationException> validate(Event event); } public interface Validator { void validate(Event event) throws ValidationException; } 这件作品看起来不错,但我对它的外观感到不舒服。我

我正在尝试实现用于处理事件的验证模块。验证模块基于简单的界面:

public interface Validator {    
    Optional<ValidationException> validate(Event event);
}
public interface Validator {    
    void validate(Event event) throws ValidationException;
}
这件作品看起来不错,但我对它的外观感到不舒服。我最关心的是将整个流处理放在一次尝试中是否是一种好的做法。此外,我不认为这种异常捕获+返回选项的混合是优雅的


对于这种情况,我可以使用一些建议和/或最佳实践。

返回异常而不是返回异常很奇怪,但不管怎样。(为什么不返回一个
ValidationResult
对象呢?异常通常被抛出并捕获)

但是您可以将私有方法更改为也返回
可选的
实例,这样可以更容易地组合它们。它还可以避免混合投掷和返回以及流。不确定这是否就是你要找的

public class ValidBallsOnlyValidator implements Validator {

    @Override
    public Optional<ValidationException> validate(Event event) 
        return event.getToys()
                .stream()
                .filter(Optional::isPresent)
                .findFirst()
                .map(ex -> new ValidationException(ex.getMessage()));
    }
    
    private Optional<InvalidToyException> validateSingleToy(Toy toy) {
        // In real code the optional here is kinda mandatory
        Optional<Toy> potentialBall = castToyToBall(toy);
        if(potentiallBall.isPresent()) {
            return checkIfBallIsOfValidSize(potentialBall.get(), "exampleSize");
        } else {
            return Optional.of(new InvalidToyException("The toy is not a ball!"));
        }
    }
    
    private Optional<InvalidToyException> checkIfBallIsOfValidSize(Toy toy, String size) {
        if(toyTooLarge(toy, size)) return Optional.of(new InvalidToyException("The ball is too big!"));
        return Optional.empty();
    }

}
public类ValidBallsOnlyValidator实现验证器{
@凌驾
公共可选验证(事件)
return事件。getToys()
.stream()
.filter(可选::isPresent)
.findFirst()
.map(例如->新建ValidationException(例如getMessage());
}
私人可选validateSingleToy(玩具玩具){
//在实数代码中,这里的可选项是强制性的
可选电位球=卡斯托球(玩具);
if(PotentialBall.isPresent()){
返回checkIfBallIsOfValidSize(potentialBall.get(),“exampleSize”);
}否则{
返回可选。of(新的InvalidToyException(“玩具不是球!”);
}
}
私人可选checkIfBallIsOfValidSize(玩具、字符串大小){
如果(ToyToolage(玩具,尺寸))返回可选的(新的无效ToyException(“球太大了!”));
返回可选的.empty();
}
}

返回异常而不是返回异常很奇怪,但无论如何。(为什么不返回一个
ValidationResult
对象呢?异常通常被抛出并捕获)

但是您可以将私有方法更改为也返回
可选的
实例,这样可以更容易地组合它们。它还可以避免混合投掷和返回以及流。不确定这是否就是你要找的

public class ValidBallsOnlyValidator implements Validator {

    @Override
    public Optional<ValidationException> validate(Event event) 
        return event.getToys()
                .stream()
                .filter(Optional::isPresent)
                .findFirst()
                .map(ex -> new ValidationException(ex.getMessage()));
    }
    
    private Optional<InvalidToyException> validateSingleToy(Toy toy) {
        // In real code the optional here is kinda mandatory
        Optional<Toy> potentialBall = castToyToBall(toy);
        if(potentiallBall.isPresent()) {
            return checkIfBallIsOfValidSize(potentialBall.get(), "exampleSize");
        } else {
            return Optional.of(new InvalidToyException("The toy is not a ball!"));
        }
    }
    
    private Optional<InvalidToyException> checkIfBallIsOfValidSize(Toy toy, String size) {
        if(toyTooLarge(toy, size)) return Optional.of(new InvalidToyException("The ball is too big!"));
        return Optional.empty();
    }

}
public类ValidBallsOnlyValidator实现验证器{
@凌驾
公共可选验证(事件)
return事件。getToys()
.stream()
.filter(可选::isPresent)
.findFirst()
.map(例如->新建ValidationException(例如getMessage());
}
私人可选validateSingleToy(玩具玩具){
//在实数代码中,这里的可选项是强制性的
可选电位球=卡斯托球(玩具);
if(PotentialBall.isPresent()){
返回checkIfBallIsOfValidSize(potentialBall.get(),“exampleSize”);
}否则{
返回可选。of(新的InvalidToyException(“玩具不是球!”);
}
}
私人可选checkIfBallIsOfValidSize(玩具、字符串大小){
如果(ToyToolage(玩具,尺寸))返回可选的(新的无效ToyException(“球太大了!”));
返回可选的.empty();
}
}
但是我对它的样子感到不舒服

您正在使用的API是疯狂的设计。处理愚蠢API的方法通常是相同的:

  • 尝试“上游”修复:提出请求,与提出请求的团队交谈,等等
  • 如果且仅当该选项已用尽时,则[A]编写您必须编写的任何丑陋的黑客程序,以使其正常工作[B]将丑陋限制在尽可能小的代码片段内;这可能需要编写一个“包含”丑陋代码的包装器,最后[C]不必担心受限的“这里丑陋没问题”区域内的代码优雅
  • API之所以奇怪,是因为它既错误地进行了验证,又没有充分利用错误带来的好处(比如,如果我错误地认为他们的方法是错误的,那么至少他们没有在方法上做得最好)

    具体来说,异常是一个返回值,从某种意义上说,它是从方法返回的一种方式。为什么这个界面不是:

    public interface Validator {    
        Optional<ValidationException> validate(Event event);
    }
    
    public interface Validator {    
        void validate(Event event) throws ValidationException;
    }
    
    更一般地说,验证不是一种“最多有一件事出错”的情况,而这正是您的问题所在,即“围绕整件事编写一个try/catch感觉很奇怪”

    很多事情都可能出错。可能有5个玩具,其中一个是球,但太大,其中一个是吱吱作响的玩具。只报告一个错误(可能是任意选择的错误)是很奇怪的

    如果您打算不抛出验证异常,而是返回验证问题,那么问题首先应该不是异常,而是其他对象,并且,您应该使用
    列表
    ,而不是
    可选
    。你已经摆脱了一个选择,这永远是一个胜利,你现在可以处理多个问题,在一次去。如果处理所有这些问题的“终点”基本上无法同时处理多个问题,那没关系:他们可以将该列表视为有效的可选选项,使用
    list.isEmpty()
    作为“一切正常”的指示器,而
    list.get(0)
    否则用于处理第一个问题(这是这个一次一个错误的系统能够处理的唯一问题)

    这涉及到代码优雅,这是定义“优雅”一词的唯一有意义的方式:它是更容易测试、更容易理解和更灵活的代码。它更灵活:如果稍后处理验证错误的端点代码被更新为能够处理多个错误,您现在可以在不接触代码t的情况下执行此操作hat使验证问题成为对象