是否可以将逻辑放入异常中

本文关键字:异常 是否 | 更新日期: 2023-09-27 17:51:17

我广泛地使用异常,我经常面临在哪里为一个非常特定的异常放置逻辑的困境。

为了说明这一点,假设我已经实现了自己的XML解析器,它接受一个文件路径,打开文件并解析它。现在,在出现解析错误的情况下,我想抛出一个异常,该异常指定文件中不正确的XML的位置。这是通过输出包含

的消息来完成的
    文件名
  1. 行号
  2. 最近的字符串内容
到目前为止,我一直试图通过仅用标量提供异常来实现这一点,因为它们可能会立即在消息中使用。这已经成为我的规则:异常中尽可能少的逻辑。按照这个规则,异常中使用的变量的任何计算都位于该异常之外。

一个简化的例子:

void parseXmlFile(string filePath)
{
    // …
    int line = currentLine();
    string surroundingText = StringHelper.getSurroundingText(filePath, offset);
    throw new SpecificXmlParseError(filePath, line, surroundingText);
}

但是随着项目的发展,我需要多次重用异常。每次我都被迫重新计算相同的变量,这违反了DRY规则。因此,我想知道是否可以将计算逻辑放入异常中,并仅为其提供计算消息数据所需的参数。它可以像这样:

class SpecificXmlParseError
{
    void SpecificXmlParseError(filePath, offset) : base(ComputeMessage(filePath, offset))
    {}
    static string ComputeMessage(filePath, offset)
    {
        int line = computeLineBasedOnOffset(filePath, offset);
        string surroundingText = StringHelper.getSurroundingText(filePath, offset);
        string exceptionMessage = putTogetherMessage(surroundingText);
        return exceptionMessage;
    }
    // ...
}
这里的风险是计算逻辑可能抛出另一个异常。但这有什么特别的原因吗?我能想到的唯一不好的事情是,它会隐藏原始异常(以及真正的原因),不让任何记录器捕捉到它们。但另一方面,在第一种情况下也会发生同样的事情,在异常之前计算数据。还有什么我忘了的问题吗?一个中间的解决方案是编写helper类,它将计算字符串消息。这是迄今为止我想到的最优雅的解决方案,但在这种情况下,它也感觉太过了,并且违反了KISS(毕竟我只想抛出一个异常!)。

有一个双赢的解决方案吗?这些解决方案的优缺点是什么?

是否可以将逻辑放入异常中

这里的风险是计算逻辑可能抛出另一个例外。但这有什么特别的原因吗?

因为异常驱动的代码可能过于复杂,难以理解,因此维护成本很高。

但是在这种情况下(出于错误处理的目的),我认为它一点也不坏。

我认为您需要做的第一件事是异常记录到日志文件或标准错误流中。那么你就可以确定问题的根源没有消失。这样,即使异常处理代码失败,您仍然有机会进行调查。

如果你的软件是关键任务,那么我会确保错误报告代码没有bug——例如,我会用大量的单元测试来覆盖它。

这里的风险是计算逻辑可能抛出另一个异常。但这有什么特别的原因吗?
我认为抛出另一个异常是任何异常处理的事实,除非你只是停止所有的执行。这取决于你想做什么。异常处理作为一种范例有其风险,但您不能完全消除它们。我不认为你的选择会让风险更大。你问题的这一部分我不一定完全理解。

对于DRY,如果您最终在处理中使用相同的逻辑,则建议创建helper类来处理错误。一个首要的设计原则是"选择你的战斗",也就是取决于你认为什么是重要的。DRY经常与KISS相冲突,但并非总是如此。它们是经验法则。设计是不确定的,是一个邪恶的问题(一旦你决定解决部分问题,你就改变了问题)。不要努力让一切都是双赢的。力争可接受

异常是检测错误并最终指定如何处理它们的逻辑的一种方法。如果逻辑变得复杂或难以维护,那么为它创建专门的(高度内聚的)类。在XML解析错误的情况下,它并没有那么复杂(您只是输出对用户帮助修复XML中的错误有用的信息)。我认为Grzegorz的回答很好地总结了这一部分,他说只要记录它。

在《容错软件模式》一书中的错误处理程序(30)模式中,Robert Hanmer说:"在特殊的处理块中分离错误处理代码,以便于维护和促进将来添加新的处理程序。"这本书有很多关于容错软件模式的建议。

报告有关XML错误的信息(文件名、行号、周围文本)的示例恰好是同一本书中Fault Observer (10)模式的一个示例。用户是这类故障的"感兴趣的观察者";这种模式中的观察者不必是系统的内部部分。

转换异常是一个模式,在有层的地方很有用。它类似于汉默的升级(9)模式。

您的应用程序无法真正恢复XML解析错误。有些方法帮助用户尝试通过猜测来修复它们。但是对于您的XML示例来说,这可能有些过分了(不确定它对您的问题有多重要)。如果您有一个用于输入的XSD,您可以获得相当好的验证和有用的错误消息(但我从未编写过它-只是在Eclipse或VisualStudio等ide中使用它)。