"switch" 相当于异常处理
"switch" equivalent for exception handling
这不是一般的异常处理问题,而是专门针对某些框架的使用结束的。几个典型起点的例子:
- GWT:
public void onFailure(Throwable caught)
AsyncCallback
接口的实现。
- JAX-RS:
public Response toResponse(E throwable)
ExceptionMapper<E extends Throwable>
接口的实现。
以上两种方法都接收到一个Throwable
的实例。通常,我见过开发人员使用一个简单的 "if/else if" 块来区分处理逻辑:
// As specified by the AsyncCallback class of the GWT framework
public void onFailure(Throwable caught) {
if (caught instanceof AnException) {
// handle AnException
} else if (caught instanceof AnotherException) {
// handle AnotherException
} else if (caught instanceof YetAnotherException) {
// handle YetAnotherException
} else if (caught instanceof ...) {
// and so on...
}
}
由于很多原因我不是 "if/else if" 块的粉丝,我想出了以下 "pattern" 将 "if/else if" 块转换为 "try/catch" 块,表现得好像它是一个 "switch" 块:
public void onFailure(Throwable caught) {
try {
throw caught;
} catch(AnException e1) {
// handle AnException
} catch(AnotherException e2) {
// handle AnotherException
} catch(YetAnotherException e3) {
// handle YetAnotherException
} catch(...) {
// and so on...
}
}
我的问题是:使用这种方法是否有任何缺点 - 在性能、最佳实践、代码可读性、一般安全性或我没有考虑或注意到的任何其他方面?
只有在抛出大量错误时,性能才重要。它不会影响成功案例中的性能。有这么多错误会比处理它们所花费的时间更重要。
如果你调用了一个本地方法,它抛出了一个异常,那么用catch块来处理它就可以了。这是做同样的事情,但使用远程方法。这不是正常的控制流,因为在到达该方法之前已经从 RPC 调用中抛出异常,因此使用通常的异常处理结构没问题。
有些检查可以由编译器完成,比如检查最具体的异常是否列在最前面,但使用一般 Throwable
类型会失去一些类型安全性。这是框架所难免的。
我可以接受这里的任何一个示例,因为它们没有太大区别。
在正常 情况下使用异常来引导程序流是一种代码味道,但这并不是您在这里所做的。我认为出于以下几个原因您可以摆脱困境:
我们已经出于各种原因捕获并重新抛出异常(例如,"catch, take some action, propagate")。这在意图上有点不同,但在成本方面并不差。
您已经承担了抛出此异常的代价至少一次。您可能已经承担了其原因被抛出、捕获、包装或重新抛出的成本。填充堆栈跟踪的成本已经支付。再次重新抛出一个已经填充的异常不会增加复杂度。
您没有使用异常来引导 正常 代码路径的流程。您正在对错误做出反应,因此您已经在 exceptional 路径上,并且您应该很少(如果有的话)在这里结束。如果此模式效率低下,除非您遇到 lots 的异常,否则这无关紧要,在这种情况下您会遇到更大的问题。花时间优化您希望采取的路径,而不是优化您不希望采取的路径。
从美学上讲,很少有东西让我的皮肤像 if/else if
块的长链一样爬行,尤其是 当条件仅仅是类型时 -检查。在我看来,您提出的建议更具可读性。有多个有序的 catch
子句很常见,因此结构 mostly 很熟悉。 try { throw e; }
序言可能是非正统的,但很容易推理。
传播时要小心Throwable
。一些错误,如 VirtualMachineError
层次结构,是出现严重错误的标志,应该允许它们 运行 他们的过程。其他人,如 InterruptedException
,传达有关 原始 线程的状态的信息,不应在 不同的 线程上盲目传播它们.有些,例如 ThreadDeath
,跨越两个类别。
您显示的两个代码块实际上在表面上非常相似:乍一看,它们都是相同的 "shape"。
值得注意的是,if/else 链实际上比 try/catch 版本的代码行更少,更容易理解。
我不认为 try/catch 版本本身是 错误的 ,但是当像这样并排比较时,我看不出有任何理由会这样更好。
在其他条件相同的情况下,没有争议的代码总是比有争议的代码更好:你永远不需要reader通过如何你选择这样做,你的代码会被什么分散注意力。
这不是一般的异常处理问题,而是专门针对某些框架的使用结束的。几个典型起点的例子:
- GWT:
public void onFailure(Throwable caught)
AsyncCallback
接口的实现。 - JAX-RS:
public Response toResponse(E throwable)
ExceptionMapper<E extends Throwable>
接口的实现。
以上两种方法都接收到一个Throwable
的实例。通常,我见过开发人员使用一个简单的 "if/else if" 块来区分处理逻辑:
// As specified by the AsyncCallback class of the GWT framework
public void onFailure(Throwable caught) {
if (caught instanceof AnException) {
// handle AnException
} else if (caught instanceof AnotherException) {
// handle AnotherException
} else if (caught instanceof YetAnotherException) {
// handle YetAnotherException
} else if (caught instanceof ...) {
// and so on...
}
}
由于很多原因我不是 "if/else if" 块的粉丝,我想出了以下 "pattern" 将 "if/else if" 块转换为 "try/catch" 块,表现得好像它是一个 "switch" 块:
public void onFailure(Throwable caught) {
try {
throw caught;
} catch(AnException e1) {
// handle AnException
} catch(AnotherException e2) {
// handle AnotherException
} catch(YetAnotherException e3) {
// handle YetAnotherException
} catch(...) {
// and so on...
}
}
我的问题是:使用这种方法是否有任何缺点 - 在性能、最佳实践、代码可读性、一般安全性或我没有考虑或注意到的任何其他方面?
只有在抛出大量错误时,性能才重要。它不会影响成功案例中的性能。有这么多错误会比处理它们所花费的时间更重要。
如果你调用了一个本地方法,它抛出了一个异常,那么用catch块来处理它就可以了。这是做同样的事情,但使用远程方法。这不是正常的控制流,因为在到达该方法之前已经从 RPC 调用中抛出异常,因此使用通常的异常处理结构没问题。
有些检查可以由编译器完成,比如检查最具体的异常是否列在最前面,但使用一般 Throwable
类型会失去一些类型安全性。这是框架所难免的。
我可以接受这里的任何一个示例,因为它们没有太大区别。
在正常 情况下使用异常来引导程序流是一种代码味道,但这并不是您在这里所做的。我认为出于以下几个原因您可以摆脱困境:
我们已经出于各种原因捕获并重新抛出异常(例如,"catch, take some action, propagate")。这在意图上有点不同,但在成本方面并不差。
您已经承担了抛出此异常的代价至少一次。您可能已经承担了其原因被抛出、捕获、包装或重新抛出的成本。填充堆栈跟踪的成本已经支付。再次重新抛出一个已经填充的异常不会增加复杂度。
您没有使用异常来引导 正常 代码路径的流程。您正在对错误做出反应,因此您已经在 exceptional 路径上,并且您应该很少(如果有的话)在这里结束。如果此模式效率低下,除非您遇到 lots 的异常,否则这无关紧要,在这种情况下您会遇到更大的问题。花时间优化您希望采取的路径,而不是优化您不希望采取的路径。
从美学上讲,很少有东西让我的皮肤像
if/else if
块的长链一样爬行,尤其是 当条件仅仅是类型时 -检查。在我看来,您提出的建议更具可读性。有多个有序的catch
子句很常见,因此结构 mostly 很熟悉。try { throw e; }
序言可能是非正统的,但很容易推理。
传播时要小心Throwable
。一些错误,如 VirtualMachineError
层次结构,是出现严重错误的标志,应该允许它们 运行 他们的过程。其他人,如 InterruptedException
,传达有关 原始 线程的状态的信息,不应在 不同的 线程上盲目传播它们.有些,例如 ThreadDeath
,跨越两个类别。
您显示的两个代码块实际上在表面上非常相似:乍一看,它们都是相同的 "shape"。
值得注意的是,if/else 链实际上比 try/catch 版本的代码行更少,更容易理解。
我不认为 try/catch 版本本身是 错误的 ,但是当像这样并排比较时,我看不出有任何理由会这样更好。
在其他条件相同的情况下,没有争议的代码总是比有争议的代码更好:你永远不需要reader通过如何你选择这样做,你的代码会被什么分散注意力。