在检查和处理对象时,请使用continue或者Checked Exceptions
我正在处理,假设是"文档"对象的列表。在成功记录文档的处理之前,我首先要检查一下几件事。假设存在引用文档的文件,并且文档中的某些内容也应存在。该示例仅进行了两个简单的检查,但是在成功处理文档之前,请考虑另外8个检查。
我们有什么喜好?
for (Document document : List<Document> documents) { if (!fileIsPresent(document)) { doSomethingWithThisResult("File is not present"); continue; } if (!isSomethingInTheDocumentPresent(document)) { doSomethingWithThisResult("Something is not in the document"); continue; } doSomethingWithTheSucces(); }
或者
for (Document document : List<Document> documents) { try { fileIsPresent(document); isSomethingInTheDocumentPresent(document); doSomethingWithTheSucces(); } catch (ProcessingException e) { doSomethingWithTheExceptionalCase(e.getMessage()); } } public boolean fileIsPresent(Document document) throws ProcessingException { ... throw new ProcessingException("File is not present"); } public boolean isSomethingInTheDocumentPresent(Document document) throws ProcessingException { ... throw new ProcessingException("Something is not in the document"); }
什么更具可读性。什么是最好的?有没有更好的方法可以做到这一点(也许使用某种设计模式)?
就可读性而言,目前我最喜欢的是Exception变量。
你是什么人
解决方案
这更多地取决于这两个条件对我们正在处理的工作流和上下文的关键程度。
如果"预期的环境"是文件存在并且每个文档中都有内容,那么我们将要抛出异常,因为这是不正常的事情,需要在特殊情况下进行处理。
如果"期望的环境"是文件来去去,有些则有内容,有些则没有,但是重要的是要了解正在传递的文件列表,然后有一个优美的继续语句,并可能记录或者发出警报较差的数据,将是可取的。
我倾向于仅在情况确实异常的情况下使用例外。
例如,作为编码人员,我们很自然地认为实际上存在一个文件可供我们处理。如果文件不存在,则我们将遇到特殊情况,并且可能无法恢复。
但是,如果文件中缺少某些内容,则可能不太严重或者更常见。例如,如果要设置初始参数列表,而用户未能指定所需的参数之一,则可以将其恢复为默认设置。这种行为被视为我们系统中的功能,因为我们向最终用户公开了一些配置脚本和属性文件。
如果抛出异常,退出处理循环,我们将无法返回。但是,如果我们仅在循环内抛出异常以指示"使用默认值,缺少规范",我认为这样做是不值得的。
简而言之,我认为这取决于。在上面的特定示例中,我认为我最终会同时使用两种样式。
使用continue,尤其是在循环中。大多数异常都会创建堆栈跟踪,这可能会在紧密循环中严重影响性能。我们需要重写fillInStackTrace以避免命中。
虽然从风格上讲,一般规则是在异常情况下应抛出异常。如果我们要查看文档是否存在并且具有某些数据,则应该继续使用,除非我们非常期望它们都将具有该数据。但是,如果文件不存在或者格式不匹配只是意外情况,请不要使用异常。
这本身与可读性无关,而与安全编程有关。如果我们检测到错误,则应确保客户端代码无法忽略它,除非它明确决定这样做(例如重试)。
同样,由于手动重构," if"语句更容易遭受逻辑意外损坏。
而且,正如我们在自己的示例中看到的那样,通过异常进行错误报告更容易阅读。
P.S.就个人而言,我更喜欢使用未经检查的异常。但这取决于我们执行的代码的类型。如果有任何异常发生,我没有理由继续,因此我只是将其提升为最高级的开始代码,并在其中进行报告。在这种情况下,未经检查的异常工作量较小。
我认为重构支票会更具可读性。
for (Document document : List<Document> documents) { if(checkDocument(document)) { doSomethingWithTheSucces(); } else { doSomethingWithTheError(); } } // Later: public boolean checkDocument(Document doc) { ... }
如果我们不需要知道为什么失败,那么checkDocument()可以只返回一个布尔值,甚至可以记录问题所在。如果代码需要知道问题出在哪里,则类似以下内容:
for (Document document : List<Document> documents) { try { checkDocument(document); doSomethingWithTheSucces(); } catch(CheckDocumentException ex) { doSomethingWithTheError(ex); } } // Later: public void checkDocument(Document doc) throws CheckDocumentException { ... }
我们也可以让checkDocument()返回某种类型的返回码而不是异常,但是我认为异常对于处理复杂的错误数据更好。
这两个选项都不需要继续或者较大的try {}块,并且通常使代码更具可读性。如果以后需要在其他地方进行检查,它还使我们可以重用逻辑来检查文档。