创建可恢复的错误接口是否比在"捕获"中处理恢复更有效



注意这个问题是基于意见的,所以我在这里用更真实的方法重新提问

因此,正如标题所说,我不确定是应该尝试实现可恢复的错误类,还是只坚持尝试在catch块中恢复。

一些要演示的伪代码:

// more or less what I have now
postToServer(data) {
try {
socket.write(data)
}
//Interrupted being a custom exception
catch(Interrupted ex) {
//happened twice is a common static method on all custom exceptions that returns a boolean indicating if the last thrown error is the same as this one
if(Interrupted.happenedTwice(ex)) throw ex;
else postToServer(data);
} 
//another custom exception indicating that the network is unreachable
catch(NetworkDrop ex) {
if(!NetworkDrop.happenedTwice(ex) && !reconnectToServer()) throw ex;
else postToServer(data);
}
}
//What I would like to implement
interface Recoverable {
public void recover(Runnable) throws Exception;
}
class NetworkDrop extends Exception implements Recoverable {
...
public void recover(Runnable resume) {
if(!NetworkDrop.happenedTwice(this) && reconnectToServer()) resume.run();
else throw this;
} 
}
class Interrupted extends Exception implements Recoverable {
...
public void recover(Runnable resume) {
if(!Interrupted.happenedTwice(this)) resume.run();
else throw this;
}     
}
postToServer(data) throws Exception {
try {
socket.write(data)
}
catch(Recoverable ex) {
ex.recover(() -> postToServer(data));
}
}

举个例子,我希望在异常中包含所有的恢复代码(减少重复代码,就像现在说的那样,5种不同的方法可以只调用recover,而不是运行相同的恢复代码(。这将允许我捕获可以恢复的异常,但仍然抛出不可恢复的异常;或者,如果无法进行恢复,则抛出。

不过,我看到了一个问题:

当从恢复中调用的中间Runnable不返回数据时,我将如何在预期返回数据的方法内恢复?假设我正在从服务器读取而不是写入,并获得可恢复的异常(如NetworkDrop(并成功恢复。由于执行是通过recover方法上的Runnable恢复的,而Runnable不会返回任何内容,那么调用read的函数将如何从Runnable中获取数据呢?

那么,这个系统会比多个catch更高效吗?或者,我从只捕捉可恢复的异常中获得的可读性和简单性是一把双刃剑吗?

如果权衡有利于可恢复类,我将如何解决从已恢复的异常中获取返回值的问题?

异常用于向调用堆栈传递故障。抛出异常特别支持这样一个方面,即在某个嵌套调用失败后,调用者通常不想继续,而是在堆栈的更上层发出失败信号。

因此,在接收到异常后,调用方会跳过其方法体的其余部分,并向其调用方发出失败信号,这种情况会在所有调用堆栈中发生,直到您找到具有适当catch块的位置。

缺陷

您的Recoverable异常将破坏该系统。如果我理解正确的话,恢复是一个双重过程:异常包含如何重新建立连接的代码,而Runnable由catch块提供,负责修复因异常而跳过的代码行。

但是由于跳过的代码将来自多个嵌套调用层,我认为您无法可靠地提供有效的恢复代码。

当然,如果您准备好在每一个软件层上用try/catch包围每一行业务代码,这会变得更容易,但这与异常处理的精神相矛盾。

为了建立您的概念,您无论如何都需要确定所有可能发生可恢复故障的地方——您必须抛出Recoverable异常,通常会包装一些原始异常。这是一种不同方法的关键。

建议

我建议使用不同的方法:

在所有抛出可恢复异常的地方,您都知道可以从故障中恢复。所以,我会在那个地方实现这些知识,而不是让方法失败,而是立即应用恢复策略,这样你就可以成功返回。

如果您担心恢复失败会浪费太多尝试,请查看";断路器";图案

最新更新