每个抛出异常的语句的 try/catch 是否被视为反模式?
我目前正在审查同事的 Java 代码,我看到很多情况下,每个可能抛出异常的语句都被封装在自己的 try/catch 中。其中 catch 块都执行相同的操作(哪个操作与我的问题无关)。
对我来说,这似乎是一种代码味道,我记得读到过它是一种常见的反模式。但是我找不到任何关于此的参考资料。
那么,对于每个抛出异常的语句,try/catch 是否都被视为反模式,支持这一点的论据是什么?
构造示例: (与原始问题无关,因此请不要介意此示例的其他问题,因为它只是为了说明我的意思而构造的。)
public int foo()
{
int x, y = 7;
try
{
x = bar(y);
}
catch(SomeException e)
{
return 0;
}
try
{
y = baz(x,y);
}
catch(SomeOtherException e)
{
return 0;
}
/* etc. */
return y;
}
(假设在这里捕获这两个异常是合适的,即我们知道做什么与他们,适当的事情是在这两种情况下返回 0。)
I am currently reviewing a colleagues Java code, and I see a lot of cases where every single statement that may throw an exception being encapsulated in its own try/catch. Where the catch block all perform the same operation (which operation is not relevant for my question).
To me this seems like a code smell, and I do remember reading about it being a common anti-pattern. However I cannot find any references on this.
So are try/catch for every single statement that throws and exception considered an anti-pattern, and what are the argument supporting this?
Constructed example:
(Does not relate to the original problem, so please don't mind other problems with this example, as it is just constructed to illustrate what I mean.)
public int foo()
{
int x, y = 7;
try
{
x = bar(y);
}
catch(SomeException e)
{
return 0;
}
try
{
y = baz(x,y);
}
catch(SomeOtherException e)
{
return 0;
}
/* etc. */
return y;
}
(Assume that it is appropriate to catch both exceptions here, i.e. we know what do with them, and the appropriate thing is to return 0 in both cases.)
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
我无法为您提供权威来源,但这些是异常处理的基本原则:
有很多关于 SO 的早期帖子处理这个问题,例如 JAVA 或 C# 中异常管理的最佳实践。
I can't serve you with an authoritative source, but these are fundamental tenets of exception handling:
There are lots of earlier posts on SO dealing with this, for example Best practices for exception management in JAVA or C#.
我最初的反应是这是一件坏事。 (另外,我最初对捕获一般异常有争论,但删除了这些争论以关注问题中给出的示例。)在异常上返回 0 在这里很难判断,因为不知道 bar 和 baz 做什么,以及异常代表什么。如果调用者确实不需要知道发生了什么错误,并且调用者不必区分作为错误条件的返回值和作为数据的返回值,那么这是可以的。另一方面,如果抛出的异常是资源不可用的问题,那么最好是快速失败,而不是继续挣扎。
我怀疑这是一件好事,因为如果这段代码在异常时返回 0 是合理的,那么 bar 和 baz 首先返回 0 至少同样合理。
一般来说,要解决捕获每个语句的异常(而不是像示例那样立即返回默认值),这是一件坏事,因为一旦抛出异常,就会出现问题,并且您的方法或对象可能会被状态不好。异常处理的目的是提供一种方法来逃离出现问题的上下文并返回到具有已知状态的位置。如果捕获每个异常并继续出错是可以的,那么我们所有的语言都将支持“On Error Resume Next”。
My initial reaction was that this is a bad thing. (Also I initially had arguments about catching exceptions in general, but removed those to focus on the example given in the question.) Returning 0 on an exception is hard to judge here because it's unknown what bar and baz do, and what the exceptions represent. If the caller really doesn't need to know anything went wrong, and the caller doesn't have to distinguish between the return value as error condition and return value as data, then this is ok. On the other hand if the exceptions thrown are problems with resources not being available it is better to fail fast than try to struggle on.
I am doubtful this is a good thing because if it is reasonable for this code to return 0 on an exception, it would have been at least as reasonable for bar and baz to return 0 in the first place.
To address catching exception for every single statement in general (as opposed to returning immediately with a default value as the example does), that is a bad thing because once you have an exception thrown, something has gone wrong and your method or object can be in a bad state. The purpose of exception-handling is to have a means of escaping a context where something has gone wrong and getting back to a place with a known state. If catching every single exception and blundering on was ok, all our languages would support `On Error Resume Next'.
魔鬼在于细节。这取决于上下文。您预计什么时候会出现这些例外情况?例如,如果 SomeException 指示非常常见的业务规则违规,并且返回 0 在该上下文中有效,那么我可以看到上面的代码非常有效。让 try..catch 块靠近导致异常的方法并不是一件坏事,因为如果将 10 个方法包装在一个巨大的 try..catch 中,那么找出哪个方法可能导致异常可能是一件很麻烦的事情。
然而,如果这些异常表明出现了严重错误并且 SomeException 不应该发生,那么返回“0”作为神奇的错误指示器隐藏可能会隐藏出现问题的情况,这可能是维护的噩梦找出问题发生的地方。
如果异常不是您可以恢复的内容,那么您不妨将其放入 throws 子句中或将其包装起来并重新抛出它(如果异常是特定于实现的,我不会像您那样将其放入 throws 子句中)不想用特定于实现的异常污染干净的接口)。另请参阅这篇关于适用的检查与非检查异常的SO文章。
不记录异常也不一定是坏事。如果异常频繁发生并且并不表明出现了严重错误,那么您不会希望每次都写入日志,否则您将“毒害日志”(即因为如果 99% 的日志条目与 SomeException 相关,那么您错过日志中的异常和/或耗尽磁盘空间的可能性是多少,因为您每天收到日志消息 50,000 次)。
The devil is in the detail. It depends on the context. When do you expect these exceptions? For example if SomeException is indicative of a business rule violation that is quite common, and returning 0 is valid in that context, the I can see the above code being quite valid. Having the try..catch block close to the method that causes it is not a bad thing, as it could be quite a chore to work out which method could cause an exception if you wrap 10 methods in one giant try..catch.
However, if these are exceptions that are indicative of something having gone very wrong and SomeException shouldn't occur, then returning "0" as a magic error indicator hides could potentially hide that something has gone wrong, the it could be a maintenance nightmare to find out where the problem occurred.
If the exception is not something that you can recover from, then you might as well put it in the throws clause or wrap it and rethrow it (if the exception is implementation specific, I wouldn't just put it in the throws clause as you wouldn't want to pollute a clean interface with Exceptions that are specific to the implementation). See also this SO article on checked vs unchecked exceptions which is applicable.
Not logging an exception is also not necessarily a bad thing. If the exception occurs very frequently and is not an indicator of something having gone terribly wrong, then you wouldn't want to write to the log every time as otherwise you'll "poison the logs" (i.e. because if 99% of log entries are related to SomeException, then what's the likelihood that you'd miss an exception in the log and/or you run out of disk space because you get the log message 50,000 times a day).
对于给定的例子,争论是它是多余的。您只需要一个
try/catch
块和一个return null
。For the given example, the argument is that it is redundant. You need just one
try/catch
block with just onereturn null
.我认为您的同事已经阅读了旧的 J2EE 最佳实践 “尽可能接近其源头捕获异常。”然后他就有点过分了。
I think your colleague has read the old J2EE best practise "Catch an exception as close as possible to its source." and then he went bit overboard with it.
事实上,我真的不认为它本身是一种反模式。但它绝对不是干净的代码,这几乎同样糟糕。
我不相信它是反模式的原因是,它从一开始似乎就不是一个好主意,这是任何东西成为反模式的要求。此外,这是编码问题的解决方案,而反模式是架构问题的通用(坏)解决方案。
I actually don't really think its an anti-pattern, per-se. But its definitely not clean code, which is almost as bad.
The reason that I don't believe its an anti-pattern is that it doesn't seem like a good idea to begin with, which is a requirement for anything to be an anti-pattern. Furthermore, this is a solution to coding problem whereas anti-patterns are general (bad) solutions to architectural problems.