我想在这里加一句,因为我看到的几乎所有Java/C#代码中的异常处理都是不正确的。例如,你最终会得到非常难以调试的错误信息,因为异常被忽略了。或者,同样糟糕的是,你可能会得到一个模糊的异常信息,它告诉你什么都不知道,因为盲目地遵循“捕获(异常)是不好的”这种想法。结果只会更糟。
首先,要理解异常是一种在代码层间传递错误信息的方式。现在,错误1: 一个层不仅仅是一个堆栈帧,它是具有明确定义职责的代码。如果你只是因为这样而编写接口和实现,那么你应该有更好的事情需要修复。
如果分层设计得当并具有特定职责,那么错误信息的意义随着其浮现而有所不同。 -这就是要做什么的关键,没有通用规则。
所以,这意味着当出现异常时,您有两个选项,但需要了解您所处的层级位置:
A) If you are in the middle of a layer, and you are just an internal, normally private, helper function and something goes bad: DONT WORRY, let the caller receive the exception. Its perfectly OK because you have no business context and
1) You are not ignoring the err或者 (huòzhě)and
2) The caller is part of your layer and should have known this can happen, but you might not now the context to handle it down below.
或者...
B) 你是这个层的顶部边界,是内部的表现。如果你遇到了异常,那么默认情况下应该捕获所有异常并阻止它们穿过到上层,因为这对调用者来说没有意义,甚至更糟的是,你可能会发生改变,调用者会依赖实现细节并且两者都会崩溃。
一个应用程序的强度在于层之间的解耦级别。通常情况下,您将停止所有操作并使用通用异常重新抛出错误,将信息转换为对上层更有意义的错误。
规则:所有进入层的入口点都应该通过CATCH ALL进行保护,并且所有的错误都应该被翻译或处理。现在仅有1%的情况会进行处理,大多数情况下,你仅需要(或只能)返回正确的抽象错误信息。
Translated: 规则:所有进入层的入口点都应该通过CATCH ALL进行保护,并且所有的错误都应该被翻译或处理。现在仅有1%的情况会进行处理,大多数情况下,你仅需要(或只能)返回正确的抽象错误信息。
不,我相信这很难理解。真实例子->
我有一个运行一些模拟的软件包。这些模拟是以文本脚本的形式存在的。有一个软件包编译这些脚本,还有一个通用的工具软件包只是读取文本文件,当然,还有基础的Java运行时库。UML依赖是->
模拟器->编译器->utilsTextLoader->Java文件
如果私有文件中的utils载入器出现故障并出现文件未找到、权限或其他错误,那么只需让其继续。没有其他的事情可以做。
2)在边界上,在初始调用utilsTextLoader函数时,您将遵循以上规则和CATCH_ALL。编译器并不在乎发生了什么,它只需要知道文件是否已加载。因此,在catch中,重新抛出新的异常并将FileNotFound或其他任何内容翻译为“无法读取文件XXXX”。
3) 编译器现在将知道源代码未被加载。这就是它需要知道的全部。因此,如果我稍后将utilsTestLoader更改为从网络加载,则编译器不会更改。如果你先放弃FileNotFound然后再更改,你将无意义地影响编译器。
4)循环重复:调用文件的较低层的实际函数在出现异常时不会进行任何操作。因此,它会向上传递异常。
由于异常在仿真器和编译器之间的层被捕获,编译器再次CATCHES_ALL,隐藏任何细节,只抛出一个更具体的错误:“无法编译脚本XXX”。
6)最后再重复一次循环,调用编译器的模拟器函数就放手不管了。
最终边界是用户。用户是一个层,所有内容都应用。主要的try语句捕获所有异常,最后创建一个漂亮的对话框或页面,并将已翻译的错误传递给用户。
所以用户可以看见。
模拟器:致命错误,无法启动模拟器。
编译器:无法编译脚本FOO1。
TextLoader:无法读取文件foo1.scp
文件未找到
相比之下:
编译器:NullPointer异常<-常见情况和一个失去的夜晚调试文件名打字错误
装载器:文件未找到 <- 我有提到过装载器要装载数百个脚本吗?
或者 (huòzhě)
c)什么也没发生,因为一切都被忽略了!!!
当然,这前提是每次重新抛出异常时,你都没有忘记设置原因异常。
好的,这是我的意见。这些简单的规则已经多次挽救了我的生命...
啤酒 (pí jiǔ)