假设您有一个方法可以检查参数 (Answer) 是否正确,并检查问题是否已经在列表中包含正确的答案:
public void addAnswer(Answer answer) {
if (answer.isCorrect()) {
...
}
}
但是,我只希望列表中的一个答案是正确的。我有多种选择。我可以抛出一个异常,我可以忽略它,我可以从 addAnswer 返回一些布尔值,告诉我操作是否正常。在这种情况下你应该怎么想?
假设您有一个方法可以检查参数 (Answer) 是否正确,并检查问题是否已经在列表中包含正确的答案:
public void addAnswer(Answer answer) {
if (answer.isCorrect()) {
...
}
}
但是,我只希望列表中的一个答案是正确的。我有多种选择。我可以抛出一个异常,我可以忽略它,我可以从 addAnswer 返回一些布尔值,告诉我操作是否正常。在这种情况下你应该怎么想?
规则很简单:对异常、错误、不可预测的故障使用异常。当您预计某事会发生或某事真的经常发生时,不要使用异常。
在您的情况下,答案不正确并不是错误或真正罕见的事情。它是您业务逻辑的一部分。您可以抛出异常,但仅作为某些验证(断言)的一部分,前提是您希望给定点的答案始终正确而突然不是(前提条件失败)。
当然,如果在检查正确性(数据库连接丢失,数组索引错误)时发生某些故障,则需要异常。
这完全取决于您想要实现的目标。您的方法的调用者是否应该已经确保它没有添加两个正确答案?如果发生这种情况,是否是编程错误的迹象?然后抛出异常,但绝对是未经检查的异常。
如果您的方法的目的是使调用者免于强制执行唯一正确答案不变量(不过我对此表示怀疑),那么您可以安排通过boolean
返回值发出信号,这使得它只是调用者的可选信息通道。
如果无法提前知道是否还有其他正确答案——例如,答案是从多个线程甚至进程(通过数据库)同时添加的——那么抛出一个检查异常是有意义的。
底线:没有一刀切的最佳实践,但对于您想要完成的每个场景都有一个最佳实践。
异常警察会像一吨砖头一样对你不利,而我会回答这个问题,并附上诸如“不要将异常用于流量控制”和“不要将异常用于正常情况”之类的声明。
第一条语句的问题在于异常是一种流控制形式。这使得论证自相矛盾,因此无效。
第二种说法的问题在于,它似乎不可避免地伴随着无休止地将异常情况重新定义为正常情况。你会在这个网站上找到例子:例如,一个生动的讨论,警察坚持认为 EOF 是“正常的”,因此不应该捕获 EOFException,尽管存在许多没有给你任何的 Java API在这件事上的选择。沿着这条路走得足够远,你最终不会有任何异常,因此根本没有机会使用它们。
这些不是合乎逻辑的论点。这些都是未经检验的教条。
最初和真正的观点,早在 1989 年首次制定时,就是你不应该向自己抛出异常,以相同的方法处理:换句话说,不要将其视为 GOTO。这一原则仍然有效。
检查异常的要点是你强制调用者做一些处理它们的事情。如果您根据自己的分析认为这是您想要的,请使用例外。或者,如果您使用的 API 会强制您捕获它们,请在适当的级别捕获它们(无论是什么:留给读者作为练习)。
换句话说,就像现实世界中的大多数事情一样,这取决于你的判断力和判断力。该功能可以像其他任何东西一样被使用或滥用。
@异常警察:你会在电话簿里找到我。但要做好争论的准备。
从方法抛出的异常会强制调用者在预期某些输入会发生异常时采取一些行动。返回值不会强制执行相同的操作,因此由调用者来捕获它并采取一些措施。
如果您希望调用者处理该场景以采取一些纠正措施,那么您应该抛出一个检查异常(子类java.lang.Exception
)。
这里的问题是您的 API 容易出错。我会改用以下方案:
public class Question {
private List<Answer> answers;
private int mCorrect;
// you may want a List implementation without duplicates
public void setAnswers(List<Answer> answers, int correct) {
this.answers = answers;
// check if int is between bounds
mCorrect = correct;
}
public boolean isCorrect(Answer answer) {
return answers.indexOf(answer) == mCorrect;
}
}
因为 aAnswer
本身只是一个陈述,并且通常不能true
与false
a 相关联Question
。这个 API 使得不可能有零个或多个正确答案,并强制用户在添加答案时提供正确的答案,因此您的程序始终处于一致状态并且根本不会失败。
在决定如何发出错误信号之前,最好设计 API 以使错误尽可能少见。使用您当前的实现,您必须在您这边进行检查,并且客户端程序员也必须在他这边进行检查。使用建议的设计,无需检查,您将拥有正确、简洁和流畅的代码。
关于何时使用 aboolean
和何时使用Exception
s,我经常看到 boolean 用于镜像底层 API(主要是低级 C 代码)。
我会以这种方式实现它:
public class Question {
private int questionId;
private final Set<Answer> options = new HashSet<Answer>();
private final Set<Answer> correctAnswers = new HashSet<Answer>();
public boolean addAnswer(Answer answer) throws WrongAnswerForThisQuestionException {
if(!answer.isValid(questionId)) {
throw new WrongAnswerForThisQuestionException(answer, this);
}
if (answer.isCorrect(questionId)) {
correctAnswers.add(answer);
}
return options.add(answer);
}
}
我同意 Tomasz Nurkiewicz 的回应。我不能对此发表评论,因为我是新用户。我还建议,如果 addAnswer() 方法并不总是会添加答案(因为它们已经存在正确的答案),请将其命名为建议这种行为。“添加”是建议正常的收藏行为。
public boolean submitAnswer(Answer answer); // returns true is answer accepted
您的确切解决方案可能取决于我们不了解的有关您的应用程序的更大图景。也许您确实想抛出一个异常,但也让调用者有责任检查添加答案是否有效。
这一切都是丰富的挂毯。