将try / catch块中的所有内容包装成防御性编程吗?

时间:2008-12-02 15:28:51

标签: exception-handling defensive-programming

过去3年我一直在编程。当我编程时,我用来处理所有已知的异常并优雅地提醒用户。我最近看到了一些代码,几乎所有方法都包含在try / catch块中。作者说它是防御性编程的一部分。我想知道,这真的是防御性编程吗?您是否建议将所有代码放入try块?

12 个答案:

答案 0 :(得分:56)

我的基本规则是:除非你能修复导致异常的问题,否则不要抓住它,让它冒泡到一个可以处理它的水平。

根据我的经验,95%的catch块只是忽略异常(catch {})或仅记录错误并重新抛出异常。后者可能看起来是正确的做法,但在实践中,当在每个级别完成此操作时,您最终只会将您的日志与同一错误消息的五个副本混杂在一起。通常这些应用程序在最顶层有一个“忽略捕获”(因为“我们已经尝试/捕获所有较低级别”),导致一个非常慢的应用程序,有很多错过的异常,并且错误日志太长了任何人都愿意透过它看。

答案 1 :(得分:19)

广泛使用Try ... Catch不是防御性编程,只是将尸体钉在直立位置

尝试...最终可以在面对意外异常时广泛用于恢复。只有当你期望一个例外,现在如何处理它时,你应该使用Try..Catch。

有时我会看到Try..Catch System.Exception,其中catch块只记录异常并重新抛出。这种方法至少有3个问题:

  • Rethrow假定一个未处理的异常,因此该程序应该终止,因为它处于未知状态。但catch会导致Catch块下面的Finally块运行。在未定义的情况下,这些Finally块中的代码可能会使问题变得更糟。
  • 这些Finally块中的代码将改变程序状态。因此,任何日志记录都不会捕获最初抛出异常时的实际程序状态。由于国家已经改变,调查将更加困难。
  • 它给你带来了悲惨的调试体验,因为调试器在重新抛出时停止,而不是原始抛出。

答案 2 :(得分:14)

不,这不是“防御性编程”。你的同事试图通过使用流行语养成良好的习惯来使他的坏习惯合理化。

他正在做的事情应该被称为“在地毯下扫地”。这就像从方法调用中统一(void) - 错误状态返回值。

答案 3 :(得分:11)

术语“防御性编程”代表以可以从错误情况中恢复或完全避免错误情况的方式编写代码。例如:

private String name;

public void setName(String name) {
}

你如何处理name == null?你抛出异常还是接受它?如果没有名称的对象没有意义,那么你应该抛出异常。那么名字==“”?

但是......后来你写了一个编辑。在设置UI时,您会发现在某些情况下,用户可以决定取消该名称,或者在用户编辑该名称时该名称可能为空。

另一个例子:

public boolean isXXX (String s) {
}

在这里,防御策略通常在s == null时返回false(尽可能避免使用NPE)。

或者:

public String getName() {
}

如果name == null,防守程序员可能会返回“”以避免调用代码中的NPE。

答案 4 :(得分:9)

如果你要处理随机异常,只在一个地方处理它们 - 应用程序的最顶层,用于:

  • 向用户展示友好讯息,
  • 保存诊断。

对于其他所有内容,您希望最直接的,特定于位置的崩溃成为可能,以便您尽早捕获这些内容 - 否则异常处理将成为隐藏草率设计和代码的一种方式。

在异常可预测的大多数情况下,可以提前测试异常处理程序将捕获的条件。

一般情况下,如果...... Else比Try ... Catch要好得多。

答案 5 :(得分:8)

捕获随机异常很糟糕。然后怎样呢?

  • 忽略他们?优秀。让我知道这对他们有用。
  • 记录它们并继续运行?我想不是。
  • 在崩溃中抛出一个不同的异常?祝你好运调试。

捕获可以实际执行有意义的事务的例外情况很好。这些案例易于识别和维护。

答案 6 :(得分:6)

我可以在这里另外说,每当我的一个同事用“抛出异常”写一个方法签名而不是列出方法真正抛出的异常类型时,我想过去拍摄它们头部?问题是,经过一段时间你有14个级别的调用,所有这些都说“抛出异常”,所以重构让他们宣布他们真正抛出的东西是一个重要的练习。

答案 7 :(得分:5)

存在“太多”处理这样的事情,并且捕捉所有例外情况都会失败。特别是对于C ++,catch(...)语句会捕获所有异常,但是您无法处理该异常的内容,因为您不知道异常的类型(它可能是任何异常)。

您应该捕获可以完全或部分处理的异常,重新抛出部分异常。你不应该抓住任何你无法处理的异常,因为这只会混淆可能(或者更确切地说)稍后会咬你的错误。

答案 8 :(得分:4)

我建议不要这样做。当您知道可抛出的异常类型时,将代码放入try-catch块是一回事。正如您所说,它允许您优雅地恢复和/或警告用户错误。但是,将所有代码放在这些块中,在这些块中您不知道可能发生的错误是使用异常来控制程序流,这是一个很大的禁忌。

如果您正在编写结构良好的代码,您将了解可能发生的每个异常,并且可以专门捕获这些异常。如果你不知道如何抛出一个特定的异常,那就不要抓住它,只是在案例中。当它发生时,你可以找出异常,导致它的原因,然后抓住它。

答案 9 :(得分:4)

在C ++中,编写大量try / catch块的一个原因是获取抛出异常的堆栈跟踪。你所做的是在任何地方写一个try / catch,并且(假设你没有处理异常处理的正确位置)让catch记录一些跟踪信息然后重新抛出异常。通过这种方式,如果一个异常一直冒泡并导致程序终止,那么你将有一个完整的堆栈跟踪,说明一切都出错(如果你不这样做,那么一个未处理的C ++异常会有帮助地解开了堆栈,并根除了你找出它来自哪里的可能性。)

我认为在任何具有更好异常处理的语言中(即未捕获的异常会告诉您它们来自何处),如果您可以对它们采取某些措施,则只希望捕获异常。否则,你只是让你的程序难以阅读。

答案 10 :(得分:3)

我猜真正的答案是“它取决于”。如果try-catch块正在捕捉非常通用的异常,那么我会说它是防御性编程,就像从来没有驱逐出你的邻居是防御性驾驶一样。 try-catch(imo)应根据特定的例外情况进行调整。

同样,这只是我的意见,但我的防御性编程概念是你需要更少/更小的try-catch块而不是更多/更大的。您的代码应该尽其所能确保异常情况永远不会存在。

答案 11 :(得分:2)

我发现“try”“catch”块非常有用,特别是在使用任何实时(例如访问数据库)时。

太多了?旁观者的眼睛。

我发现将日志复制到Word,并使用“查找”进行搜索 - 如果日志阅读器没有“查找”或“搜索”作为其包含工具的一部分 - 这是一种简单但很好的方式通过详细的日志来跋涉。

在普通意义上,它似乎肯定是“防御性的”。

我通过经验发现,无论你的经理,团队领导还是同事都这样做。如果您只是为自己编程,请使用它们直到代码“稳定”或在调试版本中,然后在完成后删除它们。