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

29 defensive-programming exception-handling

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

Jam*_*ran 57

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

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

  • 究竟.将所有内容放在try/catch语句中通常是缺乏经验的开发人员的标志,他们对异常IME知之甚少. (8认同)
  • 我认为有时候,特别是在跨越模块或库的边界时,最好捕获异常,将其包装在不同类型的异常中(如Java的cause-chaining facility),然后重新抛出. (2认同)

Roa*_*ior 19

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

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

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

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


Bil*_*win 14

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

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


Aar*_*lla 11

术语"防御性编程"代表编写代码,使其能够从错误情况中恢复或完全避免错误情况.例如:

private String name;

public void setName(String name) {
}
Run Code Online (Sandbox Code Playgroud)

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

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

另一个例子:

public boolean isXXX (String s) {
}
Run Code Online (Sandbox Code Playgroud)

在这里,防御策略通常在s == null时返回false(当你可以时避免使用NPE).

要么:

public String getName() {
}
Run Code Online (Sandbox Code Playgroud)

如果name == null,防御程序员可能返回""以避免调用代码中的NPE.


Chr*_*isA 9

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

  • 向用户呈现友好的消息,以及
  • 保存诊断.

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

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

一般来说,如果......其他比Try ... Catch要好得多.


S.L*_*ott 8

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

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

捕获实际上可以做一些有意义的事情的例外是好的.这些案例易于识别和维护.


Pau*_*lin 6

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

  • @Bill - 如果我用卷起的报纸打他们的话,还可以吗? (2认同)

Jor*_*ans 5

有一个"太多"处理的事情,捕捉所有异常有点失败的意义.特别是对于C++,catch(...)语句会捕获所有异常,但是您无法处理该异常的内容,因为您不知道异常的类型(并且它可以是任何异常).

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