2

假设您有这样的课程:

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”。有一天,您决定还允许在userNames 中使用更多符号。因此,您必须手动修复所有这 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它需要一个userNames 列表,当然您要先验证它们)
  • 不确定如何正确测试

解决方案#2

为每种类型的字段创建单独的类,以便当您谈论“用户名”时,您知道它不仅仅是 a String,它是一个 constrained 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看起来不错吗?你是怎么做到的?

4

1 回答 1

3

我肯定会选择解决方案#2。正如您在上面演示的那样,您可能希望UserNameField在不是请求的类中使用,这使得AbstractRequest该类感觉像是一个稍差的选择。
创建一个类来避免重复代码并将所有逻辑放在同一个地方对我来说听起来是个好主意,而且一点也不矫枉过正。

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

于 2012-07-11T05:10:16.357 回答