如何区分我可以向用户展示的异常,以及我不能展示的异常?

时间:2010-06-08 14:16:07

标签: language-agnostic exception-handling exception

我有一些业务逻辑可以捕获某些逻辑上无效的情况,例如:试图扭转已经逆转的交易。在这种情况下,正确的操作是通知用户:

  

交易已经逆转

  

无法撤销逆转交易

  

您无权撤消交易

  

此交易在已关闭的会话上

  

此交易太旧而无法撤销

问题是,如何将这些特殊情况传达回调用代码,以便他们向用户显示?

我是否为每种情况创建了一个单独的例外:

catch (ETransactionAlreadyReversedException)
    MessageBox.Show('Transaction already reversed')
catch (EReversingAReversingTransactionException)
    MessageBox.Show('Cannot reverse a reversing transaction')
catch (ENoPermissionToReverseTranasctionException)
    MessageBox.Show('You do not have permission to reverse transactions')
catch (ECannotReverseTransactionOnAlredyClosedSessionException)
    MessageBox.Show('This transaction is on a session that has already been closed')
catch (ECannotReverseTooOldTransactionException)
    MessageBox.Show('This transaction is too old to be reversed')

这方面的缺点是当有一个新的逻辑案例向用户显示时:

  

NSL创建的Tranasctions无法逆转

我不是简单地向用户显示一条消息,而是将其作为未处理的消息泄露出去,而实际应该使用另一个MessageBox进行处理。

另一种方法是创建一个异常类:

`EReverseTransactionException`

了解此类型的任何异常都是逻辑检查,应使用消息框处理:

catch (EReverseTransactionException)

但仍然可以理解,任何其他异常,例如涉及内存ECC奇偶校验错误的异常,都会继续未处理。

换句话说,我不会将ReverseTransaction()方法可以引发的所有错误转换为EReverseTransactionException,只会将那些逻辑上无效的错误转换为用户

5 个答案:

答案 0 :(得分:3)

我发现有各种各样的例外:

  1. TransientException - 你刚试过的东西不起作用,但是如果你再试一次它可能会。用于数据库当前不可用的情况。
  2. InvalidRequestException - 您刚要求提供一些无法完成的事情(您的示例适用于此处)
  3. SystemException - 系统生病了,我们忘记了你刚才说的一切,你的会话已经死了,你需要重新开始。
  4. 我会有这三种主要类型的异常并捕获每种异常,每种情况都有明显的具体行动。我的所有例外都来自这三种类型。

答案 1 :(得分:3)

对我来说,经验法则是用户是否有用查看确切的错误消息。这在不同类型的应用程序之间变化很大。数百万“普通用户”使用的桌面应用程序与100名经过培训的专业人员使用的企业网络应用程序完全不同。

对于前者,最好显示一般的“系统错误,请重新启动”类型的消息,而不是用户不理解的技术细节,并且通常不打算转发到支持部门(除非它只需按一下按钮即可完成。

我们的项目是后一种情况,用户通常会提出支持问题。因此,我们尝试改进错误消息,以包含与支持团队相关的信息。由于我们的是遗留应用程序,我们对内存奇偶校验错误的担忧要少于普通空指针异常和代码中的逻辑错误。

至于不同异常类型的数量,我是简单的粉丝,因此我尝试使用最少必需数量的异常类型,每个异常错误类别一个。构成单独类别的内容也由错误发生的方式和时间以及处理方式和时间来定义。由于您的案例首先与相同的用例相关联,因此我将使用具有特定详细消息的单一异常类型。

答案 2 :(得分:1)

我建议使用原因标识符的单个例外。原因本身可能是一个异常,它包含在您的用户异常中,尽管我认为这主要是出于调试目的,或者作为获取其他详细信息的方法。

您的主要例外包括一个标识符,该标识符简洁地标识用户做错了什么。它可以用作检索本地化消息以显示用户,链接到用户文档,帮助,故障排除和其他帮助的基础。

消息ID也可用作错误代码,可在报告问题时使用,也可以在支持团队的支持文档中记录解决方案。

我为所有用户级异常使用超类,允许使用ID来识别情况或原因。记录所有ID,每个ID都至少有一个测试用例来激发异常。

答案 3 :(得分:1)

您是否从基础Exception(或EException或您的语言中的等效内容中获取了所有例外情况?)

我通过从ClientException(用户提供的无效输入)或BusinessRuleException导出所有业务逻辑错误来处理此问题(输入有效,但会无意中违反某些重要的业务或域规则)。

可以捕获并向用户显示从这些根中的任何一个派生的任何异常。其他异常转到全局异常处理程序,除非代码具体知道如何处理它们。

(实际上,这并非完全准确。真正发生的是全局异常处理程序本身识别这些异常并以不同方式处理它们。但原理是相同的。)

答案 4 :(得分:1)

当您需要(或期望需要)不同类型的行为来处理不同的异常时,您应该创建单独的异常。如果您只想显示不同的消息,但所有这些消息的基本行为都相同,那么您可能只想从{{1}派生一个异常类}:

std::runtime_error

你会抛出类似的东西:

class transaction_error : public std::runtime_error { 
public:
    transaction_error(std::string const &caption) : std::runtime_error(caption) {}
};

......并抓住类似的东西:

throw transaction_error("Transaction already reversed");