2010-06-08 35 views
2

我有一些业务逻辑可以捕捉一些逻辑上无效的情况,例如试图扭转已经扭转的交易。在这种情况下,正确的操作是通知用户:如何区分我可以向用户展示的例外,以及我无法展示的例外?

交易已经逆转

不能扭转倒车交易

您没有权限扭转交易

这项交易是对已经关闭

该交易是一个会话太旧,无法逆转

问题是,我如何将这些特殊情况通知给调用代码,以便他们可以显示用户?

做我创建的每个情况下,单独的例外:

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,只有那些在逻辑上是用户的无效的原因。

回答

3

我发现有各种大类的异常:

  1. TransientException - 你刚才试了一下没有工作,但如果你再试一次它可能。用于数据库目前不可用的情况。
  2. InvalidRequestException - 你只是要求一些不能完成的事情(你的例子适合在这里)
  3. SystemException - 系统生病了,我们已经忘记了你刚才说的一切,你的会话已经死了,你需要开始再一次。

我会有这三种主要类型的异常,并抓住他们每个人,每种情况下都有明显的具体行动。我所有的例外都来自这三种类型。

+1

+1因为我真的很喜欢你对每种情况的简单描述,特别是*“系统生病了”* Aaronaught似乎同意你的看法,使用'ClientException'和'BusinessRuleException'。 **编辑:**在每种情况下,您的三个基本例外的名称是什么? – 2010-06-08 14:32:45

1

我推荐带有原因标识符的单例外。原因本身可能是一个异常,它包裹在你的用户异常中,尽管我认为这主要是为了调试目的,或者作为获取更多细节的手段。

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

消息ID作为错误代码也很有用,它可用于报告问题并记录支持团队支持文档中的解决方案。

我对所有用户级别的异常使用超类,允许使用ID来标识情况或原因。所有的ID都被记录下来,并且每个都至少有一个测试用例来引发异常。

3

对我来说,经验法则是让用户看到确切的错误信息是否有用。这在不同类型的应用程序之间变化很大。数以百万计的“普通用户”使用的桌面应用程序与由100多名训练有素的专业人员使用的企业Web应用程序完全不同。

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

我们的项目是后一种情况,而用户通常向前问题支持。因此,我们尝试改进错误消息以包含与支持团队相关的信息。由于我们是一个传统的应用程序,我们不太担心内存奇偶校验错误,而不是普通空指针异常和代码中的逻辑错误。

对于不同的异常类型的号码,我喜欢简单的粉丝,所以我尝试用异常类型,每一个不同的类错误的最小必要数量度日。什么构成一个单独的类别还由错误发生的时间和方式以及如何处理和何时处理来定义。由于您的案例首先与相同的用例相关联,因此我将使用具有特定明细消息的单一异常类型。

+0

我使用了内存ecc奇偶校验错误来避免可能发生的关于如何处理非业务规则错误的神圣战争(例如,如果存在主键冲突或TCP/IP连接失败 - 我的代码技术上可以尝试的事情处理;但我宁愿不)所以通过选择一个错误,大家一致认为我可以做*没有*关于,我得到削减追逐和专注于没有解决方案,除了“再试一次”的错误 – 2010-06-08 14:30:12

+0

@伊恩,我看到你的观点 - 这个例子起初对我来说听起来有点奇怪:-) – 2010-06-08 14:33:42

1

你从基础Exception派生所有异常的(或EException或任何等价物是在你的语言?)

我处理这从一个ClientException(用户提供无效输入导出所有的业务逻辑错误)或BusinessRuleException(输入有效,但会在不知不觉中违反一些重要的业务或域规则)。

任何来自这些根源的异常都可以捕获并显示给用户。除非代码知道如何处理它们,否则其他异常会转到全局异常处理程序。

(其实,这是不完全准确。真正的情况是,全球异常处理程序本身可以识别这些异常和不同的方式处理它们,但原理是一样的。)

+0

现在我在这个被标记的答案或djna之间撕裂了。 – 2010-06-28 23:13:31

+0

@Ian:由于这里没有客观/技术上正确的答案,所以您应该接受哪一个最能解决*您的*特殊问题。我们的答案是互补的;我主要关注*预期*异常的层次结构,而他是对所有可能异常的更广泛的分类。无论哪一个帮助你更多。 – Aaronaught 2010-06-29 00:41:07

+0

那么我回到了stackoverflow,找到我记得的答案,这给了两个例外的具体例子,以及它们被抛出时的逻辑。原来这是这个答案,而不是我之前接受的答案。另一方面,接受的答案是你的超级集合,但缺乏具体的例子。他们应该是共同的答案。 – 2010-06-29 13:58:31

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"); 

...,赶上这样的:

try { 
    execute_transaction(transaction_data); 
} 
catch(transaction_error const &e) { 
    MessageBox(NULL, e->what(), "Transaction Error", MB_OK); 
} 
相关问题