简体   繁体   English

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

[英]When is it OK to use exception handling for business logic?

I think it is accepted that as a general rule in Java (and perhaps any language with exception handling) one should try to avoid using exception handling to actually handle business logic. 我认为可以接受的是,作为Java中的一般规则(也许是任何具有异常处理的语言),应该尽量避免使用异常处理来实际处理业务逻辑。 In general, if it is expected that a certain situation is supposed to happen, one should check for it and handle it more directly than relying on exception handling to do the checking for you. 一般来说,如果预计会发生某种情况,那么应该检查它并直接处理它,而不是依赖于异常处理来为您进行检查。 For example, the following is not considered good practice: 例如,以下不被视为良好做法:

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

Instead lazy initialization should be accomplished more like this: 相反,懒惰初始化应该更像这样:

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

Of course there could be far more complex logic than simply handling lazy initialization. 当然,可能存在比简单处理延迟初始化更复杂的逻辑。 So given that this type of thing is usually frowned upon...when, if ever, is it a good idea to rely on an exception happening for certain business logic to occur? 因此,鉴于此类事情通常不受欢迎......如果有的话,依赖于某些业务逻辑发生的异常是一个好主意吗? Would it be accurate to say that any instance where one feels compelled to use this approach is really highlighting a weakness of the API being used? 是否准确地说任何一个人感到被迫使用这种方法的实例是否真的突出了所使用的API的弱点?

Whenever the exception can be anticipated but not avoided. 每当异常可以预期但不能避免时。

Say, if you are relying on an external API of some sort to parse data, and that API offers parse methods but nothing to tell whether a given input can be parsed or not (or if whether the parse can succeed or not depends on factors out of your control, but the API doesn't provide appropriate function calls), and the parsing method throws an exception when the input cannot be parsed. 比方说,如果您依赖某种外部API来解析数据,并且该API提供了解析方法,但没有什么可以判断是否可以解析给定的输入(或者解析是否成功取决于因素)您的控件,但API不提供适当的函数调用),并且解析方法在无法解析输入时抛出异常。

With a properly designed API, this should boil down to a quantity somewhere in the range "virtually never" to "never". 使用设计合理的API,这应该可以归结为“几乎从不”到“从不”范围内的某个数量。

I can see absolutely no reason to use exception handling as a means of normal flow control in code. 我完全没有理由将异常处理用作代码中正常流控制的方法。 It's expensive, it's hard to read (just look at your own first example; I realize it was probably written very quickly, but when _map hasn't been initialized, what you end up with is an empty map, throwing away the entry you were trying to add), and it litters the code with largely useless try-catch blocks, which can very well hide real problems. 这很昂贵,很难阅读(只看你自己的第一个例子;我意识到它可能写得很快,但是当_map尚未初始化时,你最终得到的是一张空地图,扔掉你的条目试图添加),它使用大量无用的try-catch块来填充代码,这可以很好地隐藏真正的问题。 Again taking your own example, what if the call to _map.add() were to throw a NullPointerException for some reason other than _map being null ? 再次以自己的例子,如果调用什么_map.add()是抛出一个NullPointerException其他某些原因_mapnull Suddenly, you are silently recreating an empty map rather than adding an entry to it. 突然间,您正在静默地重新创建一个空地图,而不是添加一个条目。 Which I'm sure I don't really have to say can lead to any number of bugs in completely unrelated places in the code because of unexpected state... 我确信我不必说,由于意外的状态,可能导致代码中完全不相关的地方出现任意数量的错误......

Edit: Just to be clear, the above answer is written in the context of Java. 编辑:为了清楚起见,上面的答案是在Java的上下文中编写的。 Other languages may (and apparently, do) differ in the implementation expense of exceptions, but other points should still hold. 其他语言可能(并且显然,确实)在异常的实现费用方面有所不同,但其他方面仍应保持不变。

Throwing an exception is a relatively expensive operation in C++, and an extremely expensive one in Java. 抛出异常是C ++中相对昂贵的操作,而且在Java中是非常昂贵的操作。 On efficiency grounds alone, it never makes sense to avoid an explicit check and accept an exception instead. 仅从效率的角度来看,避免明确检查并接受异常是没有意义的。 I suppose you might be able to justify it in some rare cases where checking whether an exception would be thrown is very complex or nearly impossible, but otherwise, I'd say the answer is "pretty much never." 我想你可能能够在极少数情况下证明它是合理的,在这种情况下,检查异常是否会被抛出是非常复杂或几乎不可能的,但除此之外,我会说答案是“几乎从来没有”。

This is really a discussion question, and the answer depends on the design of your system. 这实际上是一个讨论问题,答案取决于系统的设计。

My instinct is always a blanket never, but i've seen several systems implement business errors using exceptions. 我的直觉总是永远不会,但我看到有几个系统使用异常来实现业务错误。 I personally find it disgusting, but I really understand the advantage of breaking a business process as soon as you know that it failed, handling your failure ( eg rolling back your Unit Of Work), and returning the error to the caller, perhaps with added information. 我个人觉得这很令人作呕,但我真的理解一旦你知道它失败了,打破业务流程的优势,处理你的失败(例如回滚你的工作单元),并将错误返回给调用者,也许是添加了信息。

one possible disadvantage, is that it's really straightforward to do the error handling across several different classes, so that the definition of what happens when the process fails is really hard to deduce from the code. 一个可能的缺点是,跨几个不同的类进行错误处理非常简单,因此在进程失败时发生的事情的定义很难从代码中推断出来。

Anyway there is no single answer here, you need to weigh both approaches, and sometimes combine them. 无论如何,这里没有单一的答案,你需要权衡两种方法,有时将它们结合起来。

regarding your example, I don't see any advantages for using exceptions for flow control, especially in a "good" (designed to work) scenario. 关于您的示例,我认为使用流控制异常没有任何优势,特别是在“良好”(旨在工作)的情况下。

There is a reason exceptions are objects. 有异常是对象的原因。 There is also a reason designers of the Java language split all Throwable s into 2 main types: checked and unchecked. Java语言的设计者还将所有Throwable分为两种主要类型:checked和unchecked。

is it a good idea to rely on an exception happening for certain business logic to occur? 依赖某些业务逻辑发生的异常是一个好主意吗?

Yes. 是。 Absolutely. 绝对。 You should read Chapter 9 of "Effective Java, Second Edition". 您应该阅读“Effective Java,Second Edition”的第9章 It's all there. 一切都在那里。 Nicely explained and waiting for you. 很好地解释和等你。

If you are dealing with an error condition which is actually a part of the business logic, it is OK to use Exceptions. 如果您正在处理实际上是业务逻辑的一部分的错误条件,则可以使用Exceptions。 For example: 例如:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM