21

我有一个解析几个不同输入文件的 Java 程序。即使在此输入文件中发现错误,解析仍然可以继续并收集其他几个错误。所以我想做的不是抛出异常和停止解析过程,而是想将异常注册到某个地方,然后继续解析并以类似的方式收集其他几个错误,最后我想检查是否报告任何错误,并根据此失败或继续。

当然,我总是可以通过编写 ExceptionRegister 或任何类来手动执行此操作,但我想知道两件事:

1)这种方法有问题吗?你知道我想做的任何替代设计吗?

2)有这样做的标准方法吗?(例如,如果可能的话,我想使用内置功能,而不是滚动我自己的课程)

谢谢

编辑:我不知道为什么,但在我接受他的答案之前,有人刚刚删除了他的答案。无论如何,我认为简单的数据结构应该可以工作。基本上,我将编写一个异常类来收集多个错误消息。然后我将它称为 throw 方法,如果它至少注册了一条错误消息,它就会自行抛出。

EDIT2:这里有更多说明。我的问题与解析无关。解析只是一个例子,因为我的程序做了一些解析。想一想:我正在运行一个算法,如果出现错误,我可以继续该算法以收集更多错误,这样我就可以将这两个错误一起打印,而不是打印一个错误,当它修复后,打印第二个错误。

4

2 回答 2

7

当您无法再处理输入时,应该真正使用异常。它们是您的代码说“我放弃,我遗漏了一些信息或者我不适合这个”的特殊情况。这是一个关于如何定义此类案例的灰色区域,但正如 Bill Venners 在这篇(旧的!)文章中所说的那样,通常的哲学是:

避免使用异常来指示可以合理预期作为方法典型功能的一部分的条件。

在您的情况下,听起来您必须解析的内容可能不正确,但这是您的程序所期望的,并且没有违反足够的合同来停止解析。另一方面,例如,如果输入语法中的错误导致其余解释失败,则可以使用可接受的异常。

但是人们仍然使用异常,因为它们非常方便地停止执行并向上堆栈,而无需进入返回结果的繁琐细节。但是在它的对应物上,当您在某些对象中留下一些无人看管的状态时,它们可能会产生棘手的结果。

您的要求听起来更像是需要一种验证模式,而不是一个可能导致处理停止的异常。一个停止所有处理的异常:如果你抛出一个,其余的将被忽略。但你建议你收集它们而不是扔掉它们。所以我会说,在那种情况下,为什么要使用异常呢?看来您确实想返回正确的结果而不是停止程序的执行。

因为如果你仍然走这条路,你可能会在最后抛出一组异常。你扔哪一个?在您创建的异常收集器中,哪一个优先?

以 Eclipse 为例,它拥有这个巨大的平台来处理大量的收集插件贡献。他们使用适当的通信渠道来记录任何警告和错误,无论是在问题窗格中还是通过执行后台任务。后者的执行通常会返回一个IStatus 对象或一个变体。基于此IStatus对象,接收状态的代码决定对其进行操作。

因此,就个人而言,我会开发一个类似的对象来收集所有必要的用户错误(而不是程序错误),这不会破坏程序的执行和合同的可接受部分。这个对象可以包含错误的严重性、它的来源、关于如何修复它的提示(这可以是一个字符串,甚至是一个包含用于显示错误的精确逻辑或可能是部分自动修复的方法)等。 .. 一旦执行完成,解析的结果将获取这些状态对象并对其进行操作。如果有错误,请通过 UI 通知用户并记录。

所以它基本上与您最初建议的方法相同,减去异常和跳过可能导致讨厌的副作用和非常难以调试错误的堆栈的商品。

于 2013-11-09T02:18:50.343 回答
3

我想我现在明白了。您实际上要做的是收集解析错误(您将其表示为异常)并继续解析当前输入文件。

没有标准的方法可以做到这一点:

“异常寄存器”实际上只不过是一个解析错误描述符列表......大概是一些解析器异常。如果您可以在适当的位置捕获异常,则将其添加到“寄存器”中是微不足道的。

困难的部分是您没有谈论的功能:

  • 如何捕获错误的位置

  • 当解析器出错时如何让解析器继续解析。

这些解决方案取决于您如何实现解析器。

  • 如果您使用的是解析器生成器,那么 PGS 文档很有可能会解释如何实现它。

  • 如果您手动实现解析器,您将需要滚动自己的代码来跟踪错误位置并进行语法错误恢复。


基本上,我将编写一个异常类来收集多个错误消息。然后我将它称为 throw 方法,如果它至少注册了一条错误消息,它就会自行抛出。

这听起来像是滥用异常。一个更好的想法是将错误累积在一个列表中,然后在列表非空时创建/抛出异常。如果您想避免重复代码,请在帮助程序类中使用静态方法。

有条件地抛出自己的异常是(IMO)奇怪的!并且创建许多您不太可能抛出的异常可能是低效的。(取决于 JVM,异常的昂贵部分通常是创建异常和捕获堆栈帧。开销可能很大。)

于 2013-11-09T01:56:46.633 回答