输入验证非常复杂,原帖中提出的三种方法都是必需的,有时还需要更多。当输入超出业务逻辑的范围、数据损坏或无法读取时,异常是合适的。
如果您要检查的标志超过一两个,则标志检查很快就会成为反模式,并且可以用访问者模式的稍微专门化的版本进行替换。我不知道这个具体模式的确切名称,但我会非正式地称它为“验证器列表模式”,下面将详细描述它。
尽早检查输入并快速失败通常是好的,但并非总是可能的。通常存在大量的输入验证,来自您控制范围之外的所有输入都应视为敌对并需要验证。良好的程序设计和架构将有助于明确何时需要进行此操作。
“验证器列表模式”
作为一个例子,让我们首先在代码中描述“验证标志”反模式,然后将其转换为“验证器列表”模式。
public Optional<String> checkForErrorsUsingFlags(
ObjectToCheck objToCheck ) {
String errMsg = checkForError1( objToCheck );
if(errMsg != null ) {
return Optional.of(errMsg);
}
errMsg = checkForError2( objToCheck );
if(errMsg != null ) {
return Optional.of(errMsg);
}
return Optional.empty();
}
ObjectToCheck obj = doSomethingToReadInput(obj);
Optional<String> error = checkForErrors( obj);
if (error.isPresent()) {
} else {
}
要修复问题,首先创建一个通用接口来表示单个验证器。然后将每个检查转换为验证器实例。最后创建一个验证器列表并将其传递给验证器代码。
private interface MyValidator {
public boolean isValid(ObjectToCheck obj);
public String getErrorMessage(ObjectToCheck obj);
}
public Optional<String> checkForErrors( ObjectToCheck objToCheck,
List<MyValidator> validators ) {
for(MyValidator validator : validators ) {
if (!validator.isValid(objToCheck)) {
String errMsg = validator.getErrorMessage(objToCheck);
return Optional.of(errMsg);
}
}
return Optional.empty();
}
MyValidator validator1 = new MyValidator() {
@Override
public boolean isValid(ObjectToCheck obj) {
return checkForError1( objToCheck ) != null;
}
@Override
public boolean getErrorMessage(ObjectToCheck obj) {
return checkForError1( objToCheck );
}
}
MyValidator validator2 = new MyValidator() { ... }
List<MyValidator> validators =
ImmutableList.of( validator1, validator2);
Optional<String> error = checkForErrors(objToCheck, validators);
if (error.isPresent()) {
} else {
}
现在开始进行测试,创建一系列模拟验证器并检查每个验证器是否已调用其验证方法。您可以存根验证器结果并确保采取正确的行为。然后,您还可以单独访问每个验证器,以便可以单独测试它们。祝好运 - 希望这有所帮助,愉快地编码。