【问题标题】:Exception flow control异常流控制
【发布时间】:2017-12-08 09:34:12
【问题描述】:

我经常使用异常来进行流控制,但是我有一种奇怪的感觉,我做错了什么。编写如下所示的代码是一种好习惯吗?

public static void main(String[] args)
{
    try
    {
        methodA();
    }
    catch (Exception ex)
    {
        handleException(ex);
    }
}

public static methodA()
{
    methodB();
}

public static methodB()
{
    if(someConditionIsNotMet)
    {
        throw new RuntimeException("Some Condition Is Not Met");
    }
}

【问题讨论】:

标签: java design-patterns design-principles


【解决方案1】:

我经常使用异常来控制流

在工作流程期间抛出特定的功能异常来指示功能问题本身并不错。
这不是唯一的方法,但它是一种有效的方法。
另一种方法依赖于返回 boolean 并测试返回值的方法。
就我个人而言,我不使用这种方式,因为我发现它比较冗长、容易出错(我们不要忘记测试返回的布尔值)并且表达性较差(它只有两个值:truefalse)一个例外(它可能有尽可能多的要求)。

假设方法 B 必须检查某事,如果检查失败,则应停止处理并通知客户端该问题,为此目的使用异常是完全有效的。
现在,将异常设为特定异常而不是 Exception 会更有意义。
否则客户端如何解释异常含义?
该异常可能是工作流异常,但也可能是在运行时因其他原因引发的任何异常,例如NullPointerException
您希望以特定方式处理工作流异常,而不会将特定处理应用于其他抛出的异常。

例如,你可以写成:

public static methodA()
{
    methodB();
}

public static methodB(){
    if (!expectedDataFound()){
      throw new DataNotFoundException("data xxx was not found");
    }
    if (!hasRights()){
      throw new PermissionException("user xxx has not the rights for yyy");
    }
}

那么从客户端,你有两种方式。
单独捕获每个异常或以通用方式捕获它们(只有当它们构成同一层次结构的一部分时才有可能)。

单独捕获每个异常:

public static void main(String[] args)
{
    try
    {
        methodA();
    }
    catch (DataNotFoundException ex)
    {
        handleDataNotFoundException(ex);
    }
    catch (PermissionException ex)
    {
        handlePermissionException(ex);
    }
}

全局捕获异常:

public static void main(String[] args)
{
    try
    {
        methodA();
    }
    catch (WorkflowException ex)
    {
        handleWorkflowException(ex);
    }
}

【讨论】:

    【解决方案2】:

    我认为你说你“使用异常来控制流程”对自己太苛刻了。将异常用于控制流是一种反模式,但在您的示例中您没有。

    假设您有一个设置用户年龄的方法,当然如果调用者提供了负数,您不应该完成该操作。因此,确保这一点的一种非常合理的方法是:

    public void setAge(int age) {
        if(age <0) {
            throw new InvalidArgumentException("Age has to be zero or positive number");
        }
    }
    

    如果您不想使用异常,也许您可​​以使用语言的功能,例如 Optionals 或创建处理成功和错误的响应结构。例如,假设您有一个检索员工的方法

    public EmployeesOverview getEmployees() { ... }
    

    您的响应类可能如下所示:

    public class EmployeesOverview {
        private Ok ok;
        private Error error;
    
        class Ok {
            private Set<Employee> employees;
        }
    
        class Error {
            private String errorMessage;
        }
    }
    

    因此,在不抛出异常的情况下,您的方法将为客户提供结果,或者如果出现问题,将通知客户。

    【讨论】:

      【解决方案3】:

      我会将流控制和异常处理分开。流控制旨在确保语句以正确的顺序和在正确的条件下执行。这必须在设计时确定。异常处理是为了在运行时处理不可预见的情况。异常几乎总是由于外部因素:超时、没有磁盘空间、数据错误……

      只要我的两分钱。

      【讨论】:

        猜你喜欢
        • 2010-12-05
        • 2011-12-29
        • 2018-11-11
        • 2016-11-22
        • 2017-06-25
        • 1970-01-01
        • 1970-01-01
        • 2015-08-24
        相关资源
        最近更新 更多