简体   繁体   English

使用嵌套异常是一种好习惯吗?

[英]Is it a good practice to use nested exceptions?

This is probably a broad question, not quite SO style, but I'd still like to get some hints or guidelines if possible. 这可能是一个广泛的问题,不是那种风格,但我仍然希望得到一些提示或指导方针。

I've been looking through some legacy code and found a part of it that has methods with exceptions nested 3 or 4 levels down. 我一直在查看一些遗留代码,并发现其中一部分具有嵌套3或4级异常的方法。
Is this considered to be a normal practice or should one avoid such codestyle where possible? 这被认为是正常的做法还是应该尽可能避免这种代码风格? If it should be avoided, what are the negative effects besides the increasing costs of exception handling and decreasing readability? 如果应该避免,除了异常处理成本增加和可读性降低之外还有哪些负面影响? Are there common ways of refactoring the code to avoid this? 是否有通用的方法来重构代码以避免这种情况?

I personally prefer the following ideology 我个人更喜欢以下意识形态

Wrap Alien Exceptions 包裹异形异常

An "alien" exception is an exception thrown by a Java API or a third party library. “外来”异常是Java API或第三方库抛出的异常。 In other words, an exception you do not control. 换句话说,您无法控制的异常。

Its better to catch all alien exceptions and wrap them in an appropriate application specific exception. 最好捕获所有异常异常并将它们包装在适当的应用程序特定异常中。 Once the alien exception is converted to your own exception, you can propagate that exception any way you like. 将外来异常转换为您自己的异常后,您可以按照自己喜欢的方式传播该异常。

Rethrowing Checked Exceptions can get Messy Rethrowing Checked Exceptions可以获得凌乱

If your application uses checked exceptions, rethrowing the original exception means that the method rethrowing it must also declare it. 如果您的应用程序使用已检查的异常,则重新抛出原始异常意味着重新抛出它的方法也必须声明它。

The closer you get to the top of the call hierarchy, the more exceptions will be declared thrown. 越接近调用层次结构的顶部,将声明抛出的异常越多。 Unless you just declare all your methods to throw Exception. 除非你只是声明所有方法都抛出异常。 However, if you do so you might as well use unchecked exceptions, since you are not really getting any benefit from the compiler exception checking anyways. 但是,如果您这样做,您也可以使用未经检查的异常,因为您实际上并没有从编译器异常检查中获得任何好处。

This is why I prefer to catch non-application specific exceptions and wrap them in an application specific exception , before propagating them up the call stack. 这就是为什么我更喜欢捕获非特定于应用程序的异常并将它们包装在特定于应用程序的异常中 ,然后再将它们传播到调用堆栈中。

Guidelines For Wrapping : The context in which an exception occurs may be just as important as the location of the exception itself. 包装指南:发生异常的上下文可能与异常本身的位置一样重要。 A given location in the application may be reachable via different execution paths, and the execution path may influence the severity and cause of the error, if it occurs. 应用程序中的给定位置可以通过不同的执行路径到达,并且如果发生错误,则执行路径可以影响错误的严重性和原因。

If you need to add context information to an exception as you propagate it up the call stack, you need to use active propagation. 如果在向调用堆栈中向上传播异常时需要向异常添加上下文信息,则需要使用活动传播。 In other words, you need to catch the exception in various relevant locations on the way up the call stack, and add the relevant context information to it, before rethrowing or wrapping it. 换句话说,您需要在调用堆栈的路上在各个相关位置捕获异常,并在重新抛出或包装之前向其添加相关的上下文信息。

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.
    }

}

Exception handling tends to be an expensive way to handle flow control (certainly for C# and Java). 异常处理往往是处理流控制的一种昂贵的方法(当然对于C#和Java)。

The runtime does quite a lot of work when an exception object is constructed - getting the stack trace together, figuring out where the exception is handled and more. 构造异常对象时,运行时会做很多工作 - 将堆栈跟踪放在一起,找出处理异常的位置等等。

All this costs in memory and CPU resources that do not need to be expanded if flow control statements are used for flow control. 如果将流控制语句用于流控制,则不需要扩展内存和CPU资源的所有这些成本。

Additionally, there is a semantic issue. 此外,还存在语义问题。 Exceptions are for exceptional situations, not for normal flow control. 例外情况适用于特殊情况,而不适用于正常的流量控制。 One should use exception handling for handling unanticipated/exceptional situations, not as normal program flow, because otherwise, an uncaught exception will tell you much less. 应该使用异常处理来处理意外/异常情况,而不是正常的程序流,否则,未捕获的异常会告诉您更少。

Apart from these two, there is the matter of others reading the code. 除了这两个,还有其他人阅读代码的问题。 Using exceptions in such a manner is not something most programmers will expect, so readability and how understandable your code is suffer. 以这种方式使用异常并不是大多数程序员所期望的,因此可读性以及代码的可理解程度如何。 When one sees "Exception", one thinks - something bad happened, something that is not supposed to happen normally. 当人们看到“异常”时,人们会认为 - 发生了一些不好的事情,这种事情本来就不应该发生。 So, using exceptions in this manner is just plain confusing. 因此,以这种方式使用异常只是简单的混淆。

Please take a look at below links 请看下面的链接

Exception Handling: Common Problems and Best Practice with Java 1.4 - pdf 异常处理:Java 1.4的常见问题和最佳实践 - pdf

Why not use exceptions as regular flow of control? 为什么不将异常用作常规控制流?

Best Practices for Exception Handling 异常处理的最佳实践

Error Handling 错误处理

Mr. Google Links Google Links先生

I've been looking through some legacy code and found a part of it that has methods with exceptions nested 3 or 4 levels down. 我一直在查看一些遗留代码,并发现其中一部分具有嵌套3或4级异常的方法。

Is this considered to be a normal practice or should one avoid such codestyle where possible? 这被认为是正常的做法还是应该尽可能避免这种代码风格?

This is not a necessary process to handle your exception in this way, as it will increase your application overhead, until you really need to handle very specific exception(checked or Alien Exceptions) and you can ignore overhead to get specific information to handle that exception. 这不是以这种方式处理异常的必要过程,因为它会增加您的应用程序开销,直到您确实需要处理非常特定的异常(已检查或Alien Exceptions),并且您可以忽略开销以获取处理该异常的特定信息。

If it should be avoided, what are the negative effects besides the increasing costs of exception handling and decreasing readability? 如果应该避免,除了异常处理成本增加和可读性降低之外还有哪些负面影响?

As I mentioned you will not get specific information about the exception, if you are not going to use nested exception handling(throws with some added information to the upper handler) you may/may'not do specific action on behalf of some tough exception, but in nested case you can do action by handling that specific situation. 正如我所提到的,如果您不打算使用嵌套异常处理(向上层处理程序添加一些附加信息的抛出),您将无法获得有关异常的具体信息,您可能/可能不会代表某些棘手的异常执行特定操作,但在嵌套的情况下,您可以通过处理特定情况来执行操作。

Are there common ways of refactoring the code to avoid this? 是否有通用的方法来重构代码以避免这种情况?

If you have a poorly factored program that does what the you want and has no serious bugs, for god sake leave it alone! 如果你有一个很难实现的程序可以做你想要的并且没有严重的错误,那么为了上帝的缘故,请不要管它! When you need to fix a bug or add a feature, you Refactor Mercilessly the code that you encounter in your efforts. 当您需要修复错误或添加功能时,您将无情地重构您在工作中遇到的代码。 Override the Exception Class in your custom Exception Handler and add some added features to handle your problem. 覆盖自定义异常处理程序中的异常类,并添加一些添加的功能来处理您的问题。

The overriding method must NOT throw checked exceptions that are new or broader than those declared by the overridden method. 重写方法不得抛出新的或更广泛的已检查异常,而不是重写方法声明的异常。 For example, a method that declares a FileNotFoundException cannot be overridden by a method that declares a SQLException, Exception, or any other non-runtime exception unless it's a subclass of FileNotFoundException. 例如,声明FileNotFoundException的方法不能被声明SQLException,Exception或任何其他非运行时异常的方法覆盖,除非它是FileNotFoundException的子类。

Hop this will help you. 跳这将有助于你。

Checked Exceptions should not be propagated up the stack or chained if possible. 如果可能,不应将已检查的异常传播到堆栈或链接。 If a method is throwing a checked Exception its caller is supposed to handle it, if caller is not handling it and propagating it to its caller, then overall complexity increases. 如果一个方法抛出一个已检查的Exception,它的调用者应该处理它,如果调用者没有处理它并将它传播给它的调用者,那么整体复杂性就会增加。

In a three layered example : Dao , Service , Controller 在三层示例中:Dao,Service,Controller

DAO layer will throw DAOException Service layer should not expose DAOException to Controller , instead it should be throwing relevant BuinessExceptions, which the Controller should be handling. DAO层将抛出DAOException服务层不应该向控制器公开DAOException,而应该抛出Controller应该处理的相关BuinessExceptions。

You should do away with the exception nesting. 你应该废除异常嵌套。 You should either avoid chaining the exceptions in the first place, or (selectively) unwrap and then rethrow the nested exceptions further up the stack. 您应该首先避免链接异常,或者(有选择地)解包然后重新抛出堆栈中的嵌套异常。

About handling legacy code I would recommend you have a look at the book covering the topic: http://www.amazon.com/Working-Effectively-Legacy-Michael-Feathers/dp/0131177052 You dont even have to go through the whole book, just look at the things that concern you at the moment. 关于处理遗留代码,我建议你看看有关这个主题的书: http//www.amazon.com/Working-Effectively-Legacy-Michael-Feathers/dp/0131177052你甚至不需要经历整个预订,看看目前关注你的事情。

Also a good book regarding good practices is: 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+code 另外一本关于良好实践的好书是: 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 +代码

The best approach when handling nested exceptions is refactoring the code and using runtime instead of checked exceptions, and handling those where needed. 处理嵌套异常时的最佳方法是重构代码并使用运行时而不是已检查的异常,并在需要时处理这些异常。 This way the code is more readable and easier to maintain. 这样代码更易读,更易于维护。

Its depend on the Business logic. 它取决于业务逻辑。 You may take action on the exception there itself or you may propogate it all the way upto caller and leave it to the caller for what action he want. 你可以对那里的异常采取行动,或者你可以将它一直传播给调用者,并将其留给调用者以进行他想要的操作。

eg There are lot of third party API where they don't handle the exception but they throw it from method and hence facilitate API users to take actions as per their need. 例如,有许多第三方API,他们不处理异常,但他们从方法中抛出它,从而便于API用户根据需要采取行动。

eq oracle JDBC driver. eq oracle JDBC驱动程序。 Driver.getConnection() throws exception. Driver.getConnection()抛出异常。 Now caller/API user can handle it as per their need. 现在,调用者/ API用户可以根据需要处理它。 One may just print stack trace, one may notify admin asking for his attention or one may choose just silently exit the application. 可以只打印堆栈跟踪,可以通知管理员询问他的注意力,或者可以选择只是默默地退出应用程序。

There are two approaches: 有两种方法:

To generate a separate exception for each event.
To create a generic exception and describe what caused it 

The first approach allows you to write different code for handling the different events, but it requires you to write lot of Exception classes and in some case it could be just too much. 第一种方法允许您编写不同的代码来处理不同的事件,但它需要您编写大量的Exception类,在某些情况下它可能太多了。

The second approach is more concise, but it makes it difficult to handle the different situations. 第二种方法更简洁,但却难以处理不同的情况。

As it happens very often in programming the best solution is in the middle, where you balance generating separate exceptions and using one exception for other cases. 正如在编程中经常发生的那样,最好的解决方案是在中间,在那里平衡生成单独的异常并对其他情况使用一个例外。

The rule of the thumb in this case could be to generate a separate Exception class for the exceptions you want to handle specifically with separate code. 在这种情况下,拇指规则可以是为要使用单独代码专门处理的异常生成单独的Exception类。

Similarly to the what to throw, we should also have control on what to catch . 与投掷什么类似, 我们也应该控制要捕获的内容 We can use two approaches for our catch blocks: 我们可以为catch块使用两种方法:

A single catch block for all. 所有人都有一个捕获区。 For example: 例如:

catch (Throwable e) {
throw new CommandExecutorException(e);
}

many catch blocks one for each Exception. 每个例外的许多捕获块一个。 For example: 例如:

} catch (ClassCastException e1) {
 ...
} catch (FileNotFoundException e) {
... 
} catch (IOException e) {
...
}

The first approach is very compact but basically groups every exception in the same case, it's useful only in the situation where all the exceptions are managed equally and do the same thing. 第一种方法非常紧凑,但基本上在同一情况下对每个异常进行分组,它仅在所有异常被平等管理并执行相同操作的情况下才有用。 This approach is generally discouraged as it gives no control on the exceptions being catched, leading sometimes to tough bugs, which are also hard to find. 这种方法通常是不鼓励的,因为它无法控制被捕获的异常,有时会导致很难发现的严重错误。

The second approach requires more lines of code but you can execute different operations depending on the exception occurred. 第二种方法需要更多代码行,但您可以根据发生的异常执行不同的操作。 This approach is more flexible but in some cases leads your methods to be very long due to exception handling. 这种方法更灵活,但在某些情况下由于异常处理会导致您的方法很长。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM