【问题标题】:Design pattern for validating input?验证输入的设计模式?
【发布时间】:2013-03-04 21:34:40
【问题描述】:
验证输入的最佳方法是什么?出于论证的目的,如果输入无效,用户希望收到一条解释原因的消息。
这是我能想到的。
Validator 方法:将输入传递给验证器,如果输入有效,验证器将返回true。否则,验证器要么返回false(或错误代码)并让调用者处理无效输入。或者验证者自己负责采取行动。或者验证器调用回调方法。 缺点:在调用实际方法时,可能会重复验证所采取的步骤。
直接将输入传递给方法,无需验证。让方法自己处理无效消息。它可以直接将错误消息发送给用户,也可以使用回调方法。发送消息后,该方法必须返回或抛出异常以停止处理无效输入。调用类将继续输入下一行。 缺点:此方法现在具有发送错误消息的副作用。
这里的适当策略是什么?请注意,我不认为抛出异常是合适的,因为处理无效输入是应用程序的核心功能,至少在我的情况下。
【问题讨论】:
标签:
java
design-patterns
validation
【解决方案1】:
看看 Spring 的数据绑定和验证框架。确实非常好,并且设计为可单独使用或作为 UI 的一部分使用。
【解决方案2】:
一个很好的对象验证解决方案是使用枚举的常量功能,而无需创建许多实现“验证器”接口的类或使用不可重用的匿名内部类:
例如:
public enum ObjectValidation {
BAD_DATE_FORMAT(ErrorCode.BAD_DATE_FORMAT) {
@Override
boolean validate(String value, List<String> acceptableValues) {
boolean isValid = false;
// validate the date
SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy");
try {
sdf.parse(value);
isValid = true;
} catch (ParseException ex) {
}
/*
* We could use acceptableValues to validate the value against a
* list of acceptable values
*/
return isValid;
}
};
private ErrorCode errorCode;
private ObjectValidation(ErrorCode ErrorCode) {
this.errorCode = ErrorCode;
}
public ErrorCode getErrorCode() {
return errorCode;
}
abstract boolean validate(String value, List<String> acceptableValues);
}
以及利用枚举提供的常量验证功能并可用于同一字段的多个验证的服务方法:
public static List<ErrorCode> validate(String value, Set<ObjectValidation> objectValidators) throws Exception{
List<ErrorCode> errorCodeList = new ArrayList<ErrorCode>();
for (ObjectValidation objectValidation : objectValidators) {
if(!objectValidation.validate(value, Collections.emptyList())){
errorCodeList.add(objectValidation.getErrorCode());
}
}
return errorCodeList;
}
【解决方案4】:
老实说,您不应该将输入直接传递给方法(我猜“方法”是指一些业务逻辑),因为它结合了视图和模型。你应该做的是制作一个带有验证方法的控制器或一个单独的验证类,它将接受输入并使用一些“外部”方法对其进行验证。然后根据结果,控制器/验证器可以返回验证错误或将输入转发到您想要的任何地方。正如我之前所说,这样做的最大好处是模型和视图的解耦。模型不应该真正了解视图(如果你想改变视图怎么办?你必须重写你的模型!)。我不太确定您为什么要在业务模型中复制验证代码?验证和导航应该是控制器所做的。至少这就是 MVC 模式的工作方式。
抛出异常也不是那么糟糕,但它们应该被抛出到模型中并被控制器捕获。例如,您有一个系统,您希望用户拥有唯一的登录名,用户输入数据库中已经存在的登录名,控制器调用验证方法,该方法尝试(使用模型)将其插入数据库中。如果一切顺利,则将其插入并且控制器可以返回“您已成功插入值”消息。但是,如果模型抛出异常(如唯一约束违规错误或其他),那么控制器应该简单地捕获它并返回“此登录已存在于数据库中”。这样,模型对视图一无所知,并且可以重用,并且您没有代码重复。
【解决方案5】:
现在我正在使用 Hibernate 验证器框架,非常简单实用:
我用我需要的东西注释类然后使用它:
我的实体类:
public class Content {
@NotNull
private Heading heading;
@NotNull
@Length(max = 8)
private String contentType;
// Everything else
}
我的验证器组件:
@Component
public class Validator implements IValidator {
protected static Log log = LogFactory.getLog(Validator.class);
javax.validation.Validator validator;
@Autowired
WebApplicationExceptionBuilder webApplicationExceptionBuilder;
public void validate (Object entity) throws WebApplicationException {
ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
validator = factory.getValidator();
Set<ConstraintViolation<Object>> violations = validator.validate(entity);
if (!violations.isEmpty()) {
webApplicationExceptionBuilder
.raise("some fields are missing or incorrect",
violations);
}
}
}
在哪里使用它:
public class Foo{
@Autowired
private IValidator validator;
@Autowired
private IContentService contentService;
public void bar(Content c) throws Exception{
validator.validate(c);
contentService.persist(content);
}
}
【解决方案6】:
public interface Validator {
/**
* Validates a JComponent</code>.
* @param component
* The component to validate.
* @param message
* The message to display if the validation fails.
* @return
* Returns <code>false</code> if the validation fails, and
* <code>true</code> otherwise.
*/
boolean validate(JComponent component, String message);
}
您可以有一个 AbstractValidator(Extends javax.swing.InputVerifier) 类,它处理带有无效条目的 JComponent 旁边的消息显示。看看this例子