抛出新异常在技术上代价高昂,但是我不会对此进行大辩论,因为“代价高昂”是相对的——如果你每分钟抛出 100 个这样的异常,你可能看不到成本;如果你每秒抛出 1000 个这样的异常,你很可能会看到性能下降(因此,这里不值得讨论 - 性能是你的决定)。
我想我不得不问为什么要使用这种方法。您是否真的可以在可能引发异常的每个级别添加有意义的异常信息,如果是这样,信息是否也将是:
- 您真正想与用户分享的东西?
- 您的用户将能够解释、理解和使用的东西?
- 以不会干扰以后重用低级组件的方式编写,在编写时可能不知道它们的实用性?
我询问与您的用户共享信息的问题,因为在您的示例中,您的人工堆栈首先通知用户在数据库上进行身份验证时出现问题。对于潜在的黑客来说,这是一个很好的信息,可以揭示有关操作正在做什么的一些信息。
至于交还整个自定义异常堆栈,我认为这对大多数(诚实的)用户没有用处。例如,如果我在获取客户姓名列表时遇到问题,是否会帮助我(作为用户)知道数据库身份验证存在问题?除非您使用集成身份验证,并且您的每个用户都有一个帐户,并且能够联系系统管理员以找出他们的帐户缺少权限的原因,否则可能没有。
我将首先确定抛出的框架异常与您想提供给用户的异常消息之间是否真的存在语义差异。如果有,那么继续并在最低级别使用自定义异常(在您的示例中为“登录失败”)。接下来的步骤,直到异常的实际呈现,实际上并不需要任何自定义异常。您感兴趣的异常已经生成(登录失败) - 继续在调用堆栈的每个级别包装该消息除了将调用堆栈暴露给用户之外没有任何实际目的。对于那些“中间”步骤,假设有任何 try/catch 块,一个简单的“记录并抛出”策略就可以正常工作。
但实际上,这种策略还有另一个潜在缺陷:它强制开发人员负责维护已实施的自定义异常标准。由于在编写低级类型时您不可能知道调用层次结构的每一个排列(它们的“客户端”甚至可能还没有被编写),所有开发人员——甚至一个开发人员——似乎都不太可能记得包装和自定义每个代码块中的任何错误情况。
我通常不是从下往上工作,而是担心在进程中尽可能晚地显示抛出的异常(即尽可能靠近调用堆栈的“顶部”)。通常,我不会尝试替换在我的应用程序的低级别引发的异常中的任何消息 - 特别是因为这些低级别成员的使用往往会随着调用的深入而变得越来越抽象。我倾向于在业务层及更低的层中捕获并记录异常,然后在表示层中以易于理解的方式显示它们。
这里有几篇关于异常处理最佳实践的不错的文章:
http://www.codeproject.com/KB/architecture/exceptionbestpractices.aspx
http://aspalliance.com/1119
天哪,这太罗嗦了……提前道歉。