假设我有 3 层应用程序前端域和数据访问。我已经读过,在调用堆栈中捕获高位异常是个好主意......所以如果我得到一个数据访问异常,域层只会做一个 finally,就像这样
尝试{
}finally{ //清理 }
并让数据访问异常渗透到前端层。这不是通过让前端层处理内脏来破坏分层吗?我认为每一层都应该处理或包装并抛出它无法处理到其调用层的异常......有什么想法吗?
假设我有 3 层应用程序前端域和数据访问。我已经读过,在调用堆栈中捕获高位异常是个好主意......所以如果我得到一个数据访问异常,域层只会做一个 finally,就像这样
尝试{
}finally{ //清理 }
并让数据访问异常渗透到前端层。这不是通过让前端层处理内脏来破坏分层吗?我认为每一层都应该处理或包装并抛出它无法处理到其调用层的异常......有什么想法吗?
到目前为止有很多好的反馈,我会给你我的看法。
规则1。仅捕获您将实际处理的异常。通过句柄,我的意思是处理客户端的请求可以继续的方式。您可能会捕获足够长的时间来记录信息(不要滥用这一点,通常堆栈是足够的信息)或转换为更容易传播的不同错误(基于运行时)。但是,如果你不能处理它,不要费心去抓住它。这只是无用且令人困惑的额外代码。即使您登录或转换,您最终也会重新抛出。
意识到大多数时候,您无法处理异常。真的。许多人未能掌握这一点。但现实情况是,如果您在磁盘读取或写入时遇到 IOException,游戏就结束了。无法为用户完成该请求。如果您的网络不稳定并且您无法与数据库通信,同样的事情。
规则#2。当您确实遇到无法处理的异常时,您唯一能做的就是尝试以对用户有帮助的方式失败。这意味着,记录它以供以后分析(包括原始堆栈/原因),然后向用户报告尽可能有帮助的内容。清理您必须清理的任何内容,以便系统保持一致状态。
鉴于与最终用户的这种沟通发生在非常高的水平上,这意味着您通常必须赶上那个水平。大多数时候,我发现在它的起始点和您捕获它以记录和报告给用户的顶层之间的任何异常处理都没有什么价值。我经常转换为 RuntimeException 的一种形式,但这只是为了简化通过层的传播。
最大和最重要的是要意识到你通常无法处理异常,所以你为它们编写的任何代码都应该尽可能简单。
我不认为分层是一个如此纯粹的想法,这会破坏它。
包装和重新扔也不会增加太多价值。
让服务层处理异常有什么问题?那应该是防线的尽头,最后的防线。这种设计让服务可以一劳永逸地记录异常,并将用户友好的消息发送到 UI 以供显示。
您通常希望在调用堆栈中捕获更高的异常,但仅限于有意义的程度。如果数据级别可以处理和记录异常并将消息传递回前端,那么这将使事情变得简单和灵活。
就个人而言,如果我需要尝试和最终,那么我也想捕捉并处理那里的情况,而不是将其传递给调用者。请记住,好的设计规则总是有例外的(通常是另一个规则,如 KISS)。
这里存在三个连锁问题。
首先,可以不断地重新包装异常,但它提供了什么价值?您只是围绕原始异常创建更多层。只有当我可以提供有关异常的附加信息或第一个异常导致另一个异常时,我才会包装一个异常。
其次,异常的思想是响应一个功能不能正常完成。您应该在对处理问题最有意义的地方捕获异常。如果代码有“另一种选择”,则应在此时捕获异常。否则,将其记录下来以供用户或开发人员解决。
第三,try/finally 块。当异常会导致资源处于打开或分配状态时,这些非常有用。我总是使用 try/finally 来清理可能处于打开状态的资源(我最喜欢的是 java.sql 中的 Statement/ResultSet,一个巨大的内存消耗)。一个真正优秀的程序员在他们的代码中有很多这样的东西,作为一种优雅地恢复而不会造成巨大的内存泄漏或资源限制的方法。