7

翻译:疯狂的技术宅
原文标题:Exception handling strategy
原文链接:http://programmergate.com/exc...
本文首发微信公众号:充实的脑洞

在本文中,我们介绍了在OOP应用中处理异常的常见策略,这些策略符合最佳的异常处理技术,可以在任何应用中使用。

1. 概述

下图提供了策略概述,它展示了从检测阶段到处理阶段的异常处理流程。该图需要从下往上进行阅读:

clipboard.png

  1. Error detection: 在策略的底部是错误检测,这是发生异常的地方,它要么由程序进行检测,要么由一些外部调用引发。
  2. Local exception handling: 在第二级是本地异常处理,检测错误的类尝试在本地处理异常,例如:将请求发送到备份服务器,或等待X秒后再次尝试等...如果异常无法恢复,则将其传播到较高级别。
  3. Propagate exception to higher levels: 当本地错误处理不起作用时,该类收集诊断信息,再现和报告错误所需的所有信息,然后将该异常传到栈中。 如果检测到的异常不是低级别依赖(取决于低级别实现),那么它将被抛出,否则将被转换为自定义异常,以实现组件之间的解耦。
  4. Keep propagating if nothing to do with the exception: 较高级别的类将会继续将异常传到栈中, 只要它们与异常无关。同时关闭在传递路径的所有资源(例如文件、网络连接、释放分配的缓冲区等),并添加相关的上下文信息, 这将有助于确定错误的原因和严重性。
  5. Handle exception:在这个阶段,异常会到达一个负责处理它的类,异常所携带的所有错误信息都记录在此,并且根据异常的严重性,该类可以处理异常或者结束程序。

2. 自定义异常模板

实现异常处理策略时要做的第一件事就是,为程序的每个组件创建自定义异常,自定义异常如下所示:

public class ComponentException extends Exception {
    private static final long serialVersionUID = 1L;
    private int errorCode;
    private String errorDescription;
    private boolean isSevere;
 
    public ComponentException() {
        super();
    }
 
    public ComponentException(Exception ex) {
        super(ex);
    }
 
    public int getErrorCode() {
        return errorCode;
    }
 
    public void setErrorCode(int errorCode) {
        this.errorCode = errorCode;
    }
 
    public String getErrorDescription() {
        return errorDescription;
    }
 
    public void setErrorDescription(String errorDescription) {
        this.errorDescription = errorDescription;
    }
 
    public boolean isSevere() {
        return isSevere;
    }
 
    public void setSevere(boolean isSevere) {
        this.isSevere = isSevere;
    }
 
}

以下描述了ComponentException类的属性:

  • errorCode:识别此错误的唯一代码,errorCode告诉我们那里出错了,程序的所有错误代码应在静态类中进行预定义。该属性指示异常捕获代码应该怎样处理这个错误。
  • errorDescription: 对错误的描述,描述了用户、程序操作人员和可能的程序开发人员所需的一切必要的细节,可以使他们了解到底发生了什么错误。
  • isSevere: 指示该错误是否严重,此属性会在该异常根据错误的上下文遍历堆栈时进行更新,严重性会指示异常捕获代码是应该停止程序还是该继续处理。

3.引发异常

在检测到错误并无法从中恢复时,异常将向上传播到调用堆栈,直到到达处理它的某个 try-catch块。该异常可以按原样传递,也可转换为自定义异常。

3.1 抛出异常

如果异常不依赖于定期更改的低级实现或动态实现,那么你只需关闭打开的资源,并把异常积蓄传到调用栈而不去捕获它。下面是一个例子:

public void doSomething() throws SomeException {
    try{
        doSomethingThatCanThrowException();
    } finally {
       //close the opened resources
    }
}

3.2 抛出自定义异常

当捕获的异常取决于低级别或动态实现时,它会被转换为一个特定的异常,然后重新启动调用堆栈。 下面是一个例子:

public Student readStudent(String id) throws SomeException {
    try
    {
        // Some code which reads a student from oracle database 
    }
    catch(SQLException ex)
    {
        DataAccessException dataAccessException = new DataAccessException(ex);
        dataAccessException.setErrorCode(101); // we assume this code denotes student not found
        dataAccessException.setErrorMessage("An error occurred while reading " + 
        "student with id: " + id + " from database");
        dataAccessException.setSeverity(false);
        throw dataAccessException;
    }
}

只要它还没有到达能够处理它的类,异常就会继续传播。

P.S:强烈建议在异常传播到堆栈时更新异常的严重性,无论是异常被继续抛出,还是转换为自定义异常。

4. 捕获异常

在程序中,你需要捕获并响应来抛出异常,通常你需要在调用层次的顶部执行此操作。

捕获异常时首先要做的是记录它,通常我更喜欢使用printStackTrace(),之后的处理过程取决于异常的严重性:

  • 如果严重,则通知开发人员和程序操作人员,并且结束程序。
  • 如果不严重,则根据错误代码完成处理。通常有两种可能性,可以静默地从异常中恢复,或者通知最终用户并停止当前进程。

下面是一个例子:

try{
    startTheWholeThing();
} catch(MyAppException e) {
    e.printStackTrace();
    if(e.isSevere())
    {
        notifyNonUsers(e);
        // Halt the system gracefully        
    }
    else
    {
        if(e.getErrorCode() == 100)
        {
            // do some silent recovery logic
        }
        else
        {
            notifyUsers(e);
        }
    }
}

以上是我在程序中处理异常时所遵循的策略,希望你能喜欢。

关注微信公众号:充实的脑洞, 一个技术宅的保留地

疯狂的技术宅
44.4k 声望39.2k 粉丝