【问题标题】: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 并测试返回值的方法。
就我个人而言,我不使用这种方式,因为我发现它比较冗长、容易出错(我们不要忘记测试返回的布尔值)并且表达性较差(它只有两个值:true 和 false)一个例外(它可能有尽可能多的要求)。
假设方法 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】:
我会将流控制和异常处理分开。流控制旨在确保语句以正确的顺序和在正确的条件下执行。这必须在设计时确定。异常处理是为了在运行时处理不可预见的情况。异常几乎总是由于外部因素:超时、没有磁盘空间、数据错误……
只要我的两分钱。