何时可以对业务逻辑使用异常处理?

2022-09-01 09:51:05

我认为,作为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的弱点吗?


答案 1

每当可以预期但无法避免异常时。

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

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

我完全没有理由在代码中使用异常处理作为正常流控制的手段。它很昂贵,很难阅读(只要看看你自己的第一个例子;我意识到它可能写得很快,但是当没有被初始化时,你最终得到的是一个空的映射,扔掉了你试图添加的条目),并且它用基本上无用的尝试捕获块散落在代码中,这可以很好地隐藏真正的问题。再次以你自己为例,如果呼吁是出于某种原因而不是抛出一个怎么办?突然之间,您正在静默地重新创建一个空地图,而不是向其添加条目。我敢肯定,我真的不必说,由于意外的状态,在代码中完全不相关的地方会导致任何数量的错误......_map_map.add()NullPointerException_mapnull

编辑:为了清楚起见,上面的答案是在Java的上下文中编写的。其他语言可能(显然)在例外的实现费用方面有所不同,但其他观点仍然应该成立。


答案 2

抛出异常在C++中是一个相对昂贵的操作,在Java中也是一个非常昂贵的操作。仅出于效率考虑,避免显式检查并接受异常是没有意义的。我想在一些罕见的情况下,你也许能够证明这一点,在这些情况下,检查是否会引发异常是非常复杂或几乎不可能的,但除此之外,我会说答案是“几乎从不”。