[英]Is it a good practice to use nested exceptions?
这可能是一个广泛的问题,不是那种风格,但我仍然希望得到一些提示或指导方针。
我一直在查看一些遗留代码,并发现其中一部分具有嵌套3或4级异常的方法。
这被认为是正常的做法还是应该尽可能避免这种代码风格? 如果应该避免,除了异常处理成本增加和可读性降低之外还有哪些负面影响? 是否有通用的方法来重构代码以避免这种情况?
我个人更喜欢以下意识形态
包裹异形异常
“外来”异常是Java API或第三方库抛出的异常。 换句话说,您无法控制的异常。
最好捕获所有异常异常并将它们包装在适当的应用程序特定异常中。 将外来异常转换为您自己的异常后,您可以按照自己喜欢的方式传播该异常。
Rethrowing Checked Exceptions可以获得凌乱
如果您的应用程序使用已检查的异常,则重新抛出原始异常意味着重新抛出它的方法也必须声明它。
越接近调用层次结构的顶部,将声明抛出的异常越多。 除非你只是声明所有方法都抛出异常。 但是,如果您这样做,您也可以使用未经检查的异常,因为您实际上并没有从编译器异常检查中获得任何好处。
这就是为什么我更喜欢捕获非特定于应用程序的异常并将它们包装在特定于应用程序的异常中 ,然后再将它们传播到调用堆栈中。
包装指南:发生异常的上下文可能与异常本身的位置一样重要。 应用程序中的给定位置可以通过不同的执行路径到达,并且如果发生错误,则执行路径可以影响错误的严重性和原因。
如果在向调用堆栈中向上传播异常时需要向异常添加上下文信息,则需要使用活动传播。 换句话说,您需要在调用堆栈的路上在各个相关位置捕获异常,并在重新抛出或包装之前向其添加相关的上下文信息。
public void doSomething() throws SomeException{
try{
doSomethingThatCanThrowException();
} catch (SomeException e){
e.addContextInformation(“more info”);
throw e; //throw e, or wrap it – see next line.
//throw new WrappingException(e, “more information”);
} finally {
//clean up – close open resources etc.
}
}
异常处理往往是处理流控制的一种昂贵的方法(当然对于C#和Java)。
构造异常对象时,运行时会做很多工作 - 将堆栈跟踪放在一起,找出处理异常的位置等等。
如果将流控制语句用于流控制,则不需要扩展内存和CPU资源的所有这些成本。
此外,还存在语义问题。 例外情况适用于特殊情况,而不适用于正常的流量控制。 应该使用异常处理来处理意外/异常情况,而不是正常的程序流,否则,未捕获的异常会告诉您更少。
除了这两个,还有其他人阅读代码的问题。 以这种方式使用异常并不是大多数程序员所期望的,因此可读性以及代码的可理解程度如何。 当人们看到“异常”时,人们会认为 - 发生了一些不好的事情,这种事情本来就不应该发生。 因此,以这种方式使用异常只是简单的混淆。
请看下面的链接
我一直在查看一些遗留代码,并发现其中一部分具有嵌套3或4级异常的方法。
这被认为是正常的做法还是应该尽可能避免这种代码风格?
这不是以这种方式处理异常的必要过程,因为它会增加您的应用程序开销,直到您确实需要处理非常特定的异常(已检查或Alien Exceptions),并且您可以忽略开销以获取处理该异常的特定信息。
如果应该避免,除了异常处理成本增加和可读性降低之外还有哪些负面影响?
正如我所提到的,如果您不打算使用嵌套异常处理(向上层处理程序添加一些附加信息的抛出),您将无法获得有关异常的具体信息,您可能/可能不会代表某些棘手的异常执行特定操作,但在嵌套的情况下,您可以通过处理特定情况来执行操作。
是否有通用的方法来重构代码以避免这种情况?
如果你有一个很难实现的程序可以做你想要的并且没有严重的错误,那么为了上帝的缘故,请不要管它! 当您需要修复错误或添加功能时,您将无情地重构您在工作中遇到的代码。 覆盖自定义异常处理程序中的异常类,并添加一些添加的功能来处理您的问题。
重写方法不得抛出新的或更广泛的已检查异常,而不是重写方法声明的异常。 例如,声明FileNotFoundException的方法不能被声明SQLException,Exception或任何其他非运行时异常的方法覆盖,除非它是FileNotFoundException的子类。
跳这将有助于你。
如果可能,不应将已检查的异常传播到堆栈或链接。 如果一个方法抛出一个已检查的Exception,它的调用者应该处理它,如果调用者没有处理它并将它传播给它的调用者,那么整体复杂性就会增加。
在三层示例中:Dao,Service,Controller
DAO层将抛出DAOException服务层不应该向控制器公开DAOException,而应该抛出Controller应该处理的相关BuinessExceptions。
你应该废除异常嵌套。 您应该首先避免链接异常,或者(有选择地)解包然后重新抛出堆栈中的嵌套异常。
关于处理遗留代码,我建议你看看有关这个主题的书: http : //www.amazon.com/Working-Effectively-Legacy-Michael-Feathers/dp/0131177052你甚至不需要经历整个预订,看看目前关注你的事情。
另外一本关于良好实践的好书是: http : //www.amazon.com/Clean-Code-Handbook-Software-Craftsmanship/dp/0132350882/ref=sr_1_1?s= books&ie=UTF8&qid=1356340809&sr=1-1&keywords= clean +代码
处理嵌套异常时的最佳方法是重构代码并使用运行时而不是已检查的异常,并在需要时处理这些异常。 这样代码更易读,更易于维护。
它取决于业务逻辑。 你可以对那里的异常采取行动,或者你可以将它一直传播给调用者,并将其留给调用者以进行他想要的操作。
例如,有许多第三方API,他们不处理异常,但他们从方法中抛出它,从而便于API用户根据需要采取行动。
eq oracle JDBC驱动程序。 Driver.getConnection()抛出异常。 现在,调用者/ API用户可以根据需要处理它。 可以只打印堆栈跟踪,可以通知管理员询问他的注意力,或者可以选择只是默默地退出应用程序。
有两种方法:
To generate a separate exception for each event.
To create a generic exception and describe what caused it
第一种方法允许您编写不同的代码来处理不同的事件,但它需要您编写大量的Exception类,在某些情况下它可能太多了。
第二种方法更简洁,但却难以处理不同的情况。
正如在编程中经常发生的那样,最好的解决方案是在中间,在那里平衡生成单独的异常并对其他情况使用一个例外。
在这种情况下,拇指规则可以是为要使用单独代码专门处理的异常生成单独的Exception类。
与投掷什么类似, 我们也应该控制要捕获的内容 。 我们可以为catch块使用两种方法:
所有人都有一个捕获区。 例如:
catch (Throwable e) {
throw new CommandExecutorException(e);
}
每个例外的许多捕获块一个。 例如:
} catch (ClassCastException e1) {
...
} catch (FileNotFoundException e) {
...
} catch (IOException e) {
...
}
第一种方法非常紧凑,但基本上在同一情况下对每个异常进行分组,它仅在所有异常被平等管理并执行相同操作的情况下才有用。 这种方法通常是不鼓励的,因为它无法控制被捕获的异常,有时会导致很难发现的严重错误。
第二种方法需要更多代码行,但您可以根据发生的异常执行不同的操作。 这种方法更灵活,但在某些情况下由于异常处理会导致您的方法很长。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.