假设我们有一个具有以下构造函数的类Student
:
/** Initializes a student instance.
* @param matrNr matriculation number (allowed range: 10000 to 99999)
* @param firstName first name (at least 3 characters, no whitespace)
*/
public Student(int matrNr, String firstName) {
if (matrNr < 10000 || matrNr > 99999 || !firstName.matches("[^\\s]{3,}"))
throw new IllegalArgumentException("Pre-conditions not fulfilled");
// we're safe at this point.
}
如果我错了,请纠正我,但我认为在这个例子中,我通过简单地指定可能的输入值的(相当静态的)约束并提出一个通用的,未经检查的异常(如果那些不是)来遵循契约范式的设计满足。
现在,有一个后端课程管理学生列表,按其入学编号索引。它包含一个Map<Integer, Student>
来保存此映射,并通过addStudent
方法提供对它的访问:
public void addStudent(Student student) {
students.put(student.getMatrNr(), student);
}
现在让我们假设这个方法有一个限制,例如“数据库中必须存在具有相同入学编号的学生”。
我看到了两种可以实现这一目标的方法:
如果学生具有相同的母校,则定义UniquenessException
提升的自定义addStudent
课程。号码已经存在。调用代码将看起来像这样:
try {
campus.addStudent(new Student(...));
catch (UniquenessError) {
printError("student already existing.");
}
将要求说明为先决条件,如果不成立则只提出IAE
。此外,提供方法canAddStudent(Student stud)
,提前检查addStudent
是否会失败。调用代码看起来像这样:
Student stud = new Student(...);
if (campus.canAddStudent(stud))
campus.addStudent(stud);
else
printError("student already existing.");
从软件工程的角度来看,我认为选项A更清晰,至少出于以下原因:
因此我想知道:
addStudent
方法的前提条件?IAE
以及何时使用“正确”例外?我认为“使它成为一个先决条件,除非它取决于系统的当前状态”可能是这样的规则。还有更好的吗? UPDATE:似乎还有另一个不错的选择,即提供一个public boolean tryAddStudent(...)
方法,该方法不会抛出异常,而是使用返回值来表示错误/失败。
答案 0 :(得分:2)
(这对评论来说太长了)
在你的选项B中,我不会使用 Map&lt; Integer,Student&gt; 然后执行:
if (campus.hasStudent(12000))
printError("student already existing.");
else
campus.addStudent(new Student(...));
Map 抽象对你的用例来说不够实用(你提到并发问题),而是使用 ConcurrentMap&lt; Integer,Student&gt; 并做这样的事情:
final Student candidate = new Student(...);
final Student res = putIfAbsent(student.getMatrNr(), candidate)
if ( res != null ) {
throw new IllegalStateException("Class contract violation: \"student already exists!\", please read the doc");
}
答案 1 :(得分:2)
我不相信后端类管理学生列表的方式与合同相关 - 也就是说,它持有Map<Integer, Student>
不会成为合同的一部分。因此,在hasStudent(int matrNr)
中将预科编号纳入合同中似乎也有点恶意。
我建议校园可能应该有一个方法Boolean hasStudent(Student student)
,它会根据条件检查校园是否有学生。如果合同要求具有唯一性,并且确实非常特殊,那么您将使用合同检查:
Student student= new Student(int matrNbr, String name);
if (campus.hasStudent(student) {
throw new UniquenessException();
}
else {
campus.add(student);
}
抛出的异常应该与参数和参数以及返回值
相关<强>更新强>
如果添加应该只是失败,如果不满足唯一性并且不是例外,那么不要抛出异常。相反,使添加返回值成功(如在java.util.HashSet.add()中)。这样,如果实际添加了学生,campus.add(Student)
将返回 true 。