什么时候可以对业务逻辑使用异常处理?

时间:2011-03-21 13:09:25

标签: java exception-handling coding-style

我认为可以接受的是,作为Java中的一般规则(也许是任何具有异常处理的语言),应该尽量避免使用异常处理来实际处理业务逻辑。一般来说,如果预计会发生某种情况,那么应该检查它并直接处理它,而不是依赖于异常处理来为您进行检查。例如,以下不被视为良好做法:

try{
  _map.put(myKey, myValue);
} catch(NullPointerException e){
  _map = new HashMap<String, String>();
}

相反,延迟初始化应该更像这样:

if(_map == null){
  _map = new HashMap<String, String>();
}
_map.put(myKey, myValue);

当然,可能存在比简单处理延迟初始化更复杂的逻辑。因此,鉴于此类事情通常不受欢迎......如果有的话,依赖于某些业务逻辑发生的异常是一个好主意吗?是否准确地说任何一个人被迫使用这种方法的实例是否真的突出了所使用的API的弱点?

5 个答案:

答案 0 :(得分:31)

每当异常可以预见但不能避免时。

比方说,如果您依赖某种外部API来解析数据,并且该API提供了解析方法,但没有什么可以判断是否可以解析给定的输入(或者解析是否成功取决于取决于关于你无法控制的因素,但API没有提供适当的函数调用),并且解析方法在无法解析输入时抛出异常。

使用设计合理的API,这应该可以归结为“几乎从不”到“从不”范围内的某个数量。

我完全没有理由将异常处理用作代码中正常流控制的方法。它很昂贵,很难阅读(只看你自己的第一个例子;我意识到它可能很快写了,但是当_map尚未初始化时,你最终得到的是一张空地图,扔掉了您尝试添加的条目),它使用大量无用的try-catch块来填充代码,这可以很好地隐藏真正的问题。再举一个自己的例子,如果对_map.add()的调用由于某种原因其他而不是NullPointerException _map而引发null该怎么办?突然间,您正在静默地重新创建一个空地图,而不是添加一个条目。我确信我不必说,因为意外的状态而导致代码中完全不相关的地方出现任意数量的错误......

编辑:为了清楚起见,上面的答案是在Java的上下文中编写的。其他语言可能(并且显然,确实)在异常的实现费用方面有所不同,但其他方面仍应保留。

答案 1 :(得分:11)

抛出异常是C ++中相对昂贵的操作,而且在Java中是非常昂贵的操作。仅从效率的角度来看,避免明确检查并接受异常是没有意义的。我想你可能能够在极少数情况下证明它是合理的,在这种情况下,检查是否会抛出异常是非常复杂或几乎不可能的,但除此之外,我会说答案是“几乎从来没有。”

答案 2 :(得分:1)

这确实是一个讨论问题,答案取决于系统的设计。

我的直觉永远不是一揽子,但我发现有几个系统使用异常来实现业务错误。我个人觉得这很令人作呕,但我真的理解一旦你知道它失败了,打破业务流程的优势,处理你的失败(例如回滚你的工作单元),并将错误返回给调用者,也许是添加了信息。

一个可能的缺点是,跨多个不同的类进行错误处理非常简单,因此在进程失败时所发生的事情的定义很难从代码中推断出来。

无论如何,这里没有单一的答案,你需要权衡两种方法,有时候将它们结合起来。

关于你的例子,我没有看到使用流量控制异常的任何好处,特别是在&#34; good&#34; (旨在工作)场景。

答案 3 :(得分:0)

有一个例外是对象的原因。 Java语言的设计者还将所有Throwable分为两种主要类型:检查和未检查。

  

依靠一个是一个好主意   发生异常   业务逻辑会发生吗?

是。绝对。您应该阅读“Effective Java,Second Edition”的第9章。一切都在那里。很好地解释并等着你。

答案 4 :(得分:-2)

如果您正在处理实际上是业务逻辑一部分的错误情况,则可以使用例外。例如:

try{
   // register new user
   if(getUser(name) != null)
      throw new MyAppException("Such user already exists");
   //other registration steps......
}catch(MyAppException ex){
   sendMessage(ex.getMessage());
}