"switch" 相当于异常处理

"switch" equivalent for exception handling

这不是一般的异常处理问题,而是专门针对某些框架的使用结束的。几个典型起点的例子:

以上两种方法都接收到一个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 类型会失去一些类型安全性。这是框架所难免的。

我可以接受这里的任何一个示例,因为它们没有太大区别。

正常 情况下使用异常来引导程序流是一种代码味道,但这并不是您在这里所做的。我认为出于以下几个原因您可以摆脱困境:

  1. 我们已经出于各种原因捕获并重新抛出异常(例如,"catch, take some action, propagate")。这在意图上有点不同,但在成本方面并不差。

  2. 您已经承担了抛出此异常的代价至少一次。您可能已经承担了其原因被抛出、捕获、包装或重新抛出的成本。填充堆栈跟踪的成本已经支付。再次重新抛出一个已经填充的异常不会增加复杂度。

  3. 您没有使用异常来引导 正常 代码路径的流程。您正在对错误做出反应,因此您已经在 exceptional 路径上,并且您应该很少(如果有的话)在这里结束。如果此模式效率低下,除非您遇到 lots 的异常,否则这无关紧要,在这种情况下您会遇到更大的问题。花时间优化您希望采取的路径,而不是优化您不希望采取的路径。

  4. 从美学上讲,很少有东西让我的皮肤像 if/else if 块的长链一样爬行,尤其是 当条件仅仅是类型时 -检查。在我看来,您提出的建议更具可读性。有多个有序的 catch 子句很常见,因此结构 mostly 很熟悉。 try { throw e; } 序言可能是非正统的,但很容易推理。

传播时要小心Throwable。一些错误,如 VirtualMachineError 层次结构,是出现严重错误的标志,应该允许它们 运行 他们的过程。其他人,如 InterruptedException,传达有关 原始 线程的状态的信息,不应在 不同的 线程上盲目传播它们.有些,例如 ThreadDeath,跨越两个类别。

您显示的两个代码块实际上在表面上非常相似:乍一看,它们都是相同的 "shape"。

值得注意的是,if/else 链实际上比 try/catch 版本的代码行更少,更容易理解。

我不认为 try/catch 版本本身是 错误的 ,但是当像这样并排比较时,我看不出有任何理由会这样更好

在其他条件相同的情况下,没有争议的代码总是比有争议的代码更好:你永远不需要reader通过如何你选择这样做,你的代码会被什么分散注意力。