Java中的异常处理模板

时间:2020-01-09 10:35:58  来源:igfitidea点击:

正确的错误处理很繁琐

正确的异常处理代码编写起来可能很乏味。尝试捕获块还会使代码混乱,并使代码更难阅读。看下面的例子:

Input       input            = null;
    IOException processException = null;
    try{
        input = new FileInputStream(fileName);

        //...process input stream...
    } catch (IOException e) {
        processException = e;
    } finally {
       if(input != null){
          try {
             input.close();
          } catch(IOException e){
             if(processException != null){
                throw new MyException(processException, e,
                  "Error message..." +
                  fileName);
             } else {
                throw new MyException(e,
                    "Error closing InputStream for file " +
                    fileName;
             }
          }
       }
       if(processException != null){
          throw new MyException(processException,
            "Error processing InputStream for file " +
                fileName;
    }

在此示例中,不会丢失任何异常。如果在try块中引发了异常,而在finally块中的input.close()调用中引发了另一个异常,则这两个异常都保留在MyException实例中,并在调用堆栈中传播。

那就是在不丢失任何异常的情况下处理输入流所需的代码量。实际上,它甚至只捕获IOExceptions。如果input.close()调用也引发异常,则不会保留从try块引发的RuntimeException。是不是很丑?不难理解实际情况吗?我们还记得每次处理输入流时都要编写所有这些代码吗?

幸运的是,有一个简单的设计模式,即Template Method,可以每次都正确地处理异常,而无需在代码中看到或者编写它。好吧,也许我们将不得不编写一次,仅此而已。

我们要做的是将所有异常处理代码放入模板中。该模板只是一个普通的类。这是上述输入流异常处理的模板:

public abstract class InputStreamProcessingTemplate {

    public void process(String fileName){
        IOException processException = null;
        InputStream input = null;
        try{
            input = new FileInputStream(fileName);

            doProcess(input);
        } catch (IOException e) {
            processException = e;
        } finally {
           if(input != null){
              try {
                 input.close();
              } catch(IOException e){
                 if(processException != null){
                    throw new MyException(processException, e,
                      "Error message..." +
                      fileName);
                 } else {
                    throw new MyException(e,
                        "Error closing InputStream for file " +
                        fileName;
                 }
              }
           }
           if(processException != null){
              throw new MyException(processException,
                "Error processing InputStream for file " +
                    fileName;
        }
    }

    //override this method in a subclass, to process the stream.
    public abstract void doProcess(InputStream input) throws IOException;
}

所有异常处理都封装在InputStreamProcessingTemplate类内。注意process()方法如何在try-catch块内调用doProcess()方法。我们将通过子类化模板并覆盖doProcess()方法来使用模板。为此,我们可以编写:

new InputStreamProcessingTemplate(){
        public void doProcess(InputStream input) throws IOException{
            int inChar = input.read();
            while(inChar !- -1){
                //do something with the chars...
            }
        }
    }.process("someFile.txt");

本示例创建InputStreamProcessingTemplate类的匿名子类,实例化该子类的实例,然后调用其process()方法。

这更容易编写,也更容易阅读。在代码中只有域逻辑可见。编译器将检查我们是否正确扩展了InputStreamProcessingTemplate。通常,在编写IDE时,我们也会从IDE的代码完成中获得更多帮助,因为IDE会识别doProcess()和process()方法。

现在,我们可以在代码中需要处理文件输入流的任何地方重用InputStreamProcessingTemplate。我们可以轻松地修改模板以使其适用于所有输入流,而不仅仅是文件。

使用接口而不是子类化

可以将其重写为采用InputStreamProcessor接口的实例,而不是对InputStreamProcessingTempate进行子类化。这是它的外观:

public interface InputStreamProcessor {
    public void process(InputStream input) throws IOException;
}

public class InputStreamProcessingTemplate {

    public void process(String fileName, InputStreamProcessor processor){
        IOException processException = null;
        InputStream input = null;
        try{
            input = new FileInputStream(fileName);

            processor.process(input);
        } catch (IOException e) {
            processException = e;
        } finally {
           if(input != null){
              try {
                 input.close();
              } catch(IOException e){
                 if(processException != null){
                    throw new MyException(processException, e,
                      "Error message..." +
                      fileName;
                 } else {
                    throw new MyException(e,
                        "Error closing InputStream for file " +
                        fileName);
                 }
              }
           }
           if(processException != null){
              throw new MyException(processException,
                "Error processing InputStream for file " +
                    fileName;
        }
    }
}

注意模板的process()方法中的额外参数。这是InputStreamProcessor,它从try块(processor.process(input))内部调用。使用此模板如下所示:

new InputStreamProcessingTemplate()
        .process("someFile.txt", new InputStreamProcessor(){
            public void process(InputStream input) throws IOException{
                int inChar = input.read();
                while(inChar !- -1){
                    //do something with the chars...
                }
            }
        });

它看起来与以前的用法没有太大区别,只是对InputStreamProcessingTemplate.process()方法的调用现在更接近代码的顶部。这可能更容易阅读。

静态模板方法

也可以将模板方法实现为静态方法。这样,我们无需在每次调用模板时都将模板实例化为对象。这是InputStreamProcessingTemplate看起来是静态方法的样子:

public class InputStreamProcessingTemplate {

    public static void process(String fileName,
    InputStreamProcessor processor){
        IOException processException = null;
        InputStream input = null;
        try{
            input = new FileInputStream(fileName);

            processor.process(input);
        } catch (IOException e) {
            processException = e;
        } finally {
           if(input != null){
              try {
                 input.close();
              } catch(IOException e){
                 if(processException != null){
                    throw new MyException(processException, e,
                      "Error message..." +
                      fileName);
                 } else {
                    throw new MyException(e,
                        "Error closing InputStream for file " +
                        fileName;
                 }
              }
           }
           if(processException != null){
              throw new MyException(processException,
                "Error processing InputStream for file " +
                    fileName;
        }
    }
}

将process(...)方法简单地设置为静态。调用该方法的外观如下:

InputStreamProcessingTemplate.process("someFile.txt",
        new InputStreamProcessor(){
            public void process(InputStream input) throws IOException{
                int inChar = input.read();
                while(inChar !- -1){
                    //do something with the chars...
                }
            }
        });

注意,对模板的process()方法的调用现在是一个静态方法调用。

概括

异常处理模板是一种简单而强大的机制,可以提高代码的质量和可读性。由于我们无需编写太多的代码,也不必担心,因此它也可以提高工作效率。异常由模板处理。并且,如果我们需要在开发过程的后期改进异常处理,则只需更改一下即可:异常处理模板。

模板方法设计模式可以用于除异常处理之外的其他目的。输入流的迭代也可以放入模板中。 JDBC中ResultSet的迭代可以放在模板中。可以将正确执行JDBC中的事务放入模板中。