通过抛出异常实现Java流控制

通过抛出异常实现Java流控制,java,exception,control-flow,Java,Exception,Control Flow,我有一个关于更好的流量控制方法的问题困扰了我一段时间 我经常遇到这样的情况,我必须根据null或notnull 所以我有两个选择,我知道如何应对: 检查是否为空: public class BasedOnNullFlowControl { public String process(String login) { String redirectUri = getRedirectUri(login); if (redirectUri != null) {

我有一个关于更好的流量控制方法的问题困扰了我一段时间

我经常遇到这样的情况,我必须根据
null
notnull

所以我有两个选择,我知道如何应对:

检查是否为空:

public class BasedOnNullFlowControl {

    public String process(String login) {
        String redirectUri = getRedirectUri(login);
        if (redirectUri != null) {
            return redirectUri;
        } else {
            return "Your login is taken";
        }
    }

    private String getRedirectUri(String login) {
        Optional<Login> loginFromDb = checkLoginExists(login);
        if (loginFromDb.isPresent()) {
            return null;
        } else {
            return "some-redirect-url";
        }
    }

    private Optional<Login> checkLoginExists(String login) {
        return Optional.empty(); //assume this value comes from some API
    }

    private class Login {}
}
公共类BasedUnlullFlowControl{
公共字符串进程(字符串登录){
String redirectUri=getRedirectUri(登录名);
if(重定向URI!=null){
返回重定向URI;
}否则{
返回“您已登录”;
}
}
私有字符串getRedirectUri(字符串登录){
可选loginFromDb=checkLoginExists(登录);
if(loginFromDb.isPresent()){
返回null;
}否则{
返回“一些重定向url”;
}
}
私有可选CheckLoginList(字符串登录){
return Optional.empty();//假设此值来自某个API
}
私有类登录{}
}
或异常情况下中断流程:

public class ExceptionFlowControl {

    public String process(String login) {
        return getRedirectUri(login);
    }

    private String getRedirectUri(String login) {
        Optional<Login> loginFromDb = checkLoginExists(login);

        loginFromDb.ifPresent(l -> {
            throw new LoginExistsException();
        });
        return "some-redirect-url";
    }

    private Optional<Login> checkLoginExists(String login) {
        return Optional.empty();
    }

    private class LoginExistsException extends RuntimeException {
    }

    private class Login {}
}
公共类例外流控制{
公共字符串进程(字符串登录){
返回getRedirectUri(登录);
}
私有字符串getRedirectUri(字符串登录){
可选loginFromDb=checkLoginExists(登录);
loginFromDb.ifPresent(l->{
抛出新的LoginExistException();
});
返回“一些重定向url”;
}
私有可选CheckLoginList(字符串登录){
返回可选的.empty();
}
私有类LoginExistException扩展了RuntimeException{
}
私有类登录{}
}
我知道,异常应该只在异常情况下使用,但我的情况并不例外,而且第二种解决方案看起来更适合我,因为我可以添加一些异常处理程序(如Spring中的)并最终将其转换为一些漂亮的Http状态代码。控制器方法
过程
没有被几十个空检查所污染

你们这些聪明人能告诉我应该使用哪一种解决方案吗?或者可能还有第三个呢?

这取决于:

  • 如果在逻辑中缺少值(或null)是一种正常的和预期的情况,那么您不应该抛出异常,而应该相应地提供它。考虑使用null对象模式-而不是返回null返回某个对象,它将表示一个“非值”对象,那么您可以省略检查空值,因为您的逻辑应该相应地处理这样的空对象(通常您不需要在逻辑中编写一行代码来服务这样的对象)。
  • 如果情况是你真的无法继续,你应该抛出一个异常。应使用异常来表示应用程序中的错误。如果您可以事先避免异常(例如使用
    file.exists()
    )检查用户是否指定了正确的文件名),那么您应该这样做(除非它确实超出了正常的逻辑流程,并且您无法继续)

在您的示例中,您真的不应该抛出异常,因为用户输入错误登录是正常(和预期)情况,这应该由正常逻辑流处理


在您的特定情况下,为什么不返回重定向uri,该uri指向带有“用户不存在”消息类型的某个页面?

异常方法允许代码部分之间的依赖性较小,因此第一种方法非常差。使用它,您需要从
checkLoginExists()
过程传递一个加载了特殊含义的
null
值(
null==login take

但是,引发异常不是
getRedirectUri()
的工作,应该在
checkLoginExists()
中完成。我怀疑
getRedirectUri()
是否也要负责检查登录名是否存在。更不用说使用
null
Optional
只会给您一个成功/失败的二元概念。如果登录存在,但被锁定,那么您必须禁止登录,并创建同名的新用户。您可能希望重定向到其他页面以指示登录已锁定

不过,这是基于意见的,高度依赖于具体情况,没有明确的规则可以遵循

编辑(现在所有的喧闹都结束了):这是一个被广泛接受的观点,正常的流量控制不应该在例外情况下进行。然而,法线的定义并不那么容易定义。您不会像这样编写代码

int[] array = ...
int i = 0;
try {
    while(true) {
       array[i]++;
       i++;
    }
} catch(ArrayIndexOutOfBoundsException e) {
    // Loop finished
}
然而,如果你没有使用像上面这样一个可笑的简单例子,它就会进入灰色区域

还要注意,异常与应用程序错误不同。试图通过验证所有内容来解决异常是不可能的(或者至少是不明智的)。如果我们考虑新用户试图登记用户名的情况,我们希望防止重复用户名。您可以检查副本并显示错误消息,但两个并发请求仍有可能试图注册相同的用户名。此时,其中一个将出现异常,因为数据库将不允许重复(如果您的系统在任何情况下都是合理的)

验证很重要,但异常并没有什么真正的例外。您需要优雅地处理它们,因此,如果最终不得不处理异常,为什么还要麻烦验证呢。如果我们以我在评论中写的某个例子为例,您需要防止重复和诅咒单词作为用户名,您可能会想到类似的方法(假设这是在
getRedirectUri()


首先,如果使用optionals,可以完全避免
null
检查:

public class BasedOnNullFlowControl {

    public String process(String login) {
        String redirectUri = 
        return getRedirectUri(login).orElse("Your login is taken");
    }

    private Optional<String> getRedirectUri(String login) {
        return checkLoginExists(login).map(ifExists -> "some-redirect-url");
    }

    private Optional<Login> checkLoginExists(String login) {
        return Optional.empty(); //assume this value comes from some API
    }

    private class Login {}
}
公共类BasedUnlullFlowControl{
公共字符串进程(字符串登录){
public class BasedOnNullFlowControl {

    public String process(String login) {
        String redirectUri = 
        return getRedirectUri(login).orElse("Your login is taken");
    }

    private Optional<String> getRedirectUri(String login) {
        return checkLoginExists(login).map(ifExists -> "some-redirect-url");
    }

    private Optional<Login> checkLoginExists(String login) {
        return Optional.empty(); //assume this value comes from some API
    }

    private class Login {}
}