异常层次结构 vs 错误枚举

8

我曾在某个地方(现在找不到了)读到过,庞大的异常层次结构是浪费时间的。当时这种说法的理由听起来很有道理,这个想法一直留在了我的脑海中。

在我自己的代码中,当我有一个可能出现各种错误情况的代码库时,我会使用单个异常并使用枚举成员来区分它们。

如果出现需要捕获这些错误之一的情况,我会捕获它,检查枚举值,如果不是我期望的值就重新抛出异常。理想情况下,这种情况应该很少发生。

最近我又开始处理异常,我对自己的异常习惯产生了反思。我很好奇其他人做什么以及为什么这样做?

使用层次结构还是一个带有数据成员的异常?

顺便说一句,我假设你同意使用异常而不是错误码的想法。我不想打开那个话题。

8个回答

13

简单的经验法则:

  • 如果你在检查异常后最终重新抛出它们,那么你需要更细粒度的异常层次结构(除了检查过程涉及相当逻辑的罕见情况外)。
  • 如果你有永远不会被捕获的异常类(只有它们的超类型被捕获),那么你需要一个较少细粒度的异常层次结构。

11

我认为仅使用一个带有嵌入枚举的异常类型是次优的:

  1. 这会强制你比必要更频繁地捕获异常(一旦你确定无法处理它,就重新抛出它)。
  2. 枚举本身成为了一个变化的焦点(所有程序员都需要定期修改它)。
  3. 枚举不具有层次结构,因此例如您不能轻松地使用一个处理程序处理所有IO错误。
  4. 有时,除了错误消息文本之外,您还想向调用者提供其他附加信息,例如未找到的文件的路径或从SQL服务器接收到的错误号码。将来自所有可能的错误情况的这些附加信息放入同一异常中使得处理变得更加麻烦。

回答您的问题,我使用异常的层次结构。它比深度更广泛。


7

在这方面不应该固守一种观念,要根据手头的问题使用最合适的工具。我的经验法则如下:

  • 只有当你需要一个需要不同行为的异常时才创建新的异常类。
  • 相反,当你只想将额外信息与异常一起包含时,请将内容(枚举、错误代码等)添加到异常类中。
  • 除非确实需要它们,否则不要创建新类。记住,YAGNI…

4
我认为你处于两个最糟糕的境地之一。一个庞大的异常层次结构是无用的,因为客户端通常很懒惰,最终只会检查顶级节点(可能仅检查层次结构根节点)。你的枚举系统并没有解决这个问题,并使异常捕获系统更加繁琐。
如果你确实知道需要许多不同的异常,并且捕获者真正需要这些不同的异常(而不是模糊地想),那么请使用大型层次结构,忘记枚举。否则,请坚持使用小型异常层次结构,并仅提供对于捕获者真正有趣的异常类。

3
根据我的经验,过多的异常类会使人感到困惑,特别是当它们只用于一种非常特定的问题类型时。我曾见过一个系统,每个错误条件都有自己的异常,但在 catch 块中只检查了超类。子类完全浪费了时间。
另一方面,必须检查枚举并重新抛出异常会使代码不够直观,因为你无法直接在 catch 后识别抛出的异常。这可能只是一个小缺点,但如果经常使用,会对可读性产生影响。在需要非常快速的代码中使用时,这也可能带来性能问题。
我不会使用枚举解决方案,而是使用几个异常类型,这些类型的范围足够广泛,以便于大多数开发人员使用。例如,使用 DatabaseException 而不是 TableNotFoundException、ColumnNotFoundexception 等多个异常。根据我的经验,这对大多数开发者来说效果最好。你可以随时添加一个字段,其中包含一些错误代码,以便于用户和支持之间的交流。

2
您不能通过用一个异常类型和枚举子类型来替换过多的异常类来解决问题。事实上,这只会使问题更加严重!
例如:假设您有异常类型Ex1..Ex99,其中E14..E18是E1的子类等等。现在您决定用一个异常Ex和子类型ST1..ST99来代替它们。您解决了什么?什么也没有——人们仍然必须处理所有可能性。您让情况变得更糟了吗?您不能忽略ST14-ST18并将它们视为ST1的发生。现在,您必须处理所有99种可能性,因为您的枚举子类型没有自然、灵活的层次结构。

1

不要将错误与异常混淆。

错误是常见的,应该尽可能在问题附近解决。

异常很少发生,通常需要更多上下文信息才能解决。

如果出现问题并且可以在本地纠正,则应该使用错误代码并在那里处理。

异常应该用于将问题传播到调用堆栈中具有足够信息以解决问题的上下文中。

话虽如此:枚举不是正确的方法,因为它们绕过了编译器自动生成的整个try catch机制。


0

我喜欢使用少量的异常来处理不同类型的错误,例如致命/非致命条件或应用程序特定的层或模块。异常抛出方应提供包含异常有效负载代码的信息。应用程序的表示层可以查找与代码相对应的本地化人类可读的错误消息。

总之,异常类型对开发人员有用,而与错误代码相对应的消息对用户有用。


2
@Andrew:关于异常的“致命/非致命”分类,应该在捕获异常时确定,而不是抛出异常时!因此,它与异常类型无关。 - Daniel Paull

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接