Java中的异常处理模板
正确的错误处理很繁琐
正确的异常处理代码编写起来可能很乏味。尝试捕获块还会使代码混乱,并使代码更难阅读。看下面的例子:
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中的事务放入模板中。