在多个类中使用相同的约束

时间:2012-07-11 04:57:14

标签: java validation

假装你有一个类似这样的课程:

public class CreateUserRequest {
  ...
  @NotNull
  @Size(min = 4, max = 16)
  @Pattern(regexp = "^[a-zA-Z0-9]+$")
  private final String userName;
  ...
}

我们这里有一个字段“userName”,它有一些约束,所以有些值可以,有些则不行。您还有一个班级EditUserRequest。您已将用户userName用作用户标识符,因此您肯定会在EditUserRequest中包含此字段:

public class EditUserRequest {
  ...
  @NotNull
  @Size(min = 4, max = 16)
  @Pattern(regexp = "^[a-zA-Z0-9]+$")
  private final String userName;
  ...
}

当然,您希望能够删除用户:

public class DeleteUserRequest {
  ...
  @NotNull
  @Size(min = 4, max = 16)
  @Pattern(regexp = "^[a-zA-Z0-9]+$")
  private final String userName;
  ...
}

所有这些类都有一个字段“userName”,具有绝对相似的含义和类似的约束。有一天,您决定还要允许在userName中使用更多符号。因此,您必须手动修复所有这3个类。

解决方案#1

为所有这些请求创建基类:

public abstract AbstractUserRequest {
  ...
  @NotNull
  @Size(min = 4, max = 16)
  @Pattern(regexp = "^[a-zA-Z0-9]+$")
  private final String userName;
  ...  
}

public class CreateUserRequest extends AbstractUserRequest {
  ... // OK, we have userName here
}

赞成

  • 您现在只需要修复约束
  • 创建新类型的请求非常简单,只需将AbstractUserRequest子类化,它就可以工作。

缺点

  • 如果您在其他地方需要userName,则必须拥有AbstractUserRequest的子类(例如:CreateUserGroupRequest,需要userName的列表,确定你想先验证它们)
  • 没有准确确定如何正确测试

解决方案#2

为每种类型的字段创建单独的类,这样当您谈论“用户名”时,您知道它不仅仅是String,而是受约束的String

public class UserNameField {
  @NotNull
  @Size(min = 4, max = 16)
  @Pattern(regexp = "^[a-zA-Z0-9]+$")
  private final String value;
  ...
}

public class CreateUserRequest {
  ...
  private UserNameField userName;
  ...
}

赞成

  • 如果用户名约束发生变化,您只需修改一次
  • 你有一个易于测试的简单课程

缺点

  • 感觉像矫枉过正(?)

解决方案#3(更新)

Hibernate验证器允许约束组合,因此您可以为一组约束命名:

@NotNull
@Size(min = 4, max = 16)
@Pattern(regexp = "^[a-zA-Z0-9]+$")
@Target( { METHOD, FIELD, ANNOTATION_TYPE })
@Retention(RUNTIME)
@Constraint(validatedBy = {})
@Documented
public @interface UserName {
  String message() default "{com.loki2302.constraints.username}";
  Class<?>[] groups() default {};
  Class<? extends Payload>[] payload() default {};
}
...
public class CreateUserRequest {
  ...
  @UserName
  private final String userName;
  ...
}

优点:

  • 简单而有力的方法
  • 易于使用(无额外抽象)
  • 易于测试

缺点:

  • 无可能?

这里的常用方法是什么? 解决方案#2 看起来不错吗? 怎么做?

1 个答案:

答案 0 :(得分:3)

我肯定会选择解决方案#2 。如上所示,您可能希望在非请求的类中使用UserNameField,这会使AbstractRequest类感觉更糟糕。 创建一个类来避免重复代码并将所有逻辑放在同一个地方听起来对我来说是一个很好的主意,并且根本不会过分。

[添加解决方案#3后编辑]
解决方案#3 看起来像解决方案#2 的非常好的变体。我无法真正决定哪一个我更喜欢,他们都能很好地封装逻辑。