在java/spring中,为多个用例验证同一个bean的最佳方法是什么



TL;DR:从";干净代码";从这个角度来看,对于不同的用例,对任何对象实现验证的最佳方式是什么

为了澄清,假设我有这个实体:

public class User {
private Integer id;

private String username;
//getters, setters ...
}

和这个服务类(它也可以是一个接口(:

public class UserService{
public void createUser(User user){
//...
}
public void updateUser(User user){
//...
}
} 

和这两个用例:

1.创建用户(id必须为空,用户名必须为有效用户名(

2.更新用户(id必须是有效id,用户名必须是有效用户名(




  • 第一种方法(NAIVE方法(:将每个用例的验证逻辑放入服务方法中。例如:

    public void createUser(User user){
    //validation logic here
    //other business logic
    }
    



  • 第二种方法(使用注释(:使用hibernate的开箱即用注释(@NotNull@Null…(或hibernate自定义注释(例如,在这种情况下,我可以创建一个注释@Username,根据我的应用程序需求验证用户名(。在这种情况下,User看起来像:公共类用户{

    @Id
    private Integer id;
    @Username
    private String username;
    //getters, setters ...
    }
    //and the service methods will have `@Valid` before the parameters to    be validated:
    public class UserService{
    public void createUser(@Valid User user){
    //...
    }
    public void updateUser(@Valid User user){
    //...
    }
    } 
    

这里的问题是,相同的验证逻辑应用于用例创建和更新,这不是我上面的要求所暗示的




  • 第三种方法(对每个用例使用带有验证组的注释(:

    public class User {
    @Id(OnUpdate.class)
    @Null(OnCreate.class)
    private Integer id;
    @Username //no need to specify groups bcz this field has same validation logic for all cases
    private String username;
    //getters, setters ...
    }
    

    这里的问题是,实体现在违反了单一责任原则,并且知道它将如何在不同的情况下使用案例


  • 第四种方法(手动调用验证器(:

    与第一种方法相同,但验证逻辑未投入使用方法,但是我从服务手动调用Validator方法:

    public class UserService{
    public void createUser(User user){
    //call validator.validate(user) here
    //other business logic
    }
    public void updateUser(User user){
    //call validator.validate(user) here
    //other business logic
    }
    } 
    

    n.b.:用户被注释为第三种方法,即使用验证组


  • 最后一种方法:

    使用功能编程类型:EitherRightLeft、,ValueObject(我们可以从依赖项中获得它们的实现像vavr.io,因为它们还不支持开箱即用(

    abstract class ValueObject<L,R>{
    Either<L,R> value;
    //L (left) for storing invalid value
    //R (right) for storing valid value
    }
    class UsernameVO extends ValueObject<ValueFailure<String>,String>{
    private UsernameVO(Either<ValueFailure<String>,String> value){
    this.value = value;
    }
    public static UsernameVO create(String name){
    //factory method calls the private constructor
    //...
    }
    //other private methods for validation
    }
    class ValueFailure<T>{
    String reason;
    T failedValue;
    //getters, setters...
    }
    

    所以现在我们在User中有一个字段UsernameVO username,而不是简单String username

    在这种情况下,服务看起来像:

    public class UserService{
    public void createUser(User user){
    //use "fold" method on each Either type to validate it
    user
    .usernameVO.
    .value
    .fold(stringValueFailure -> {
    //throw error
    },
    validValue -> {
    //do nothing
    return null;
    });
    }
    public void updateUser(User user){
    //same as above
    }
    }
    

对于bean验证的所有用例,没有最好的方法。你需要权衡利弊。违反你所举例子的单一责任原则不应该造成太大的问题。但是,如果您的bean验证有超过2/3的不同情况,并且实体更大,那么我将建议以下方法。

public class User {
// Add hibernate annotations only for the common cases. 
private Integer id; 
@UserName  
private String username;
//getters, setters ...
}

然后使用下面的类进行更全面的验证。

public class UserValidator {
public static final int CREATE = 0;
public static final int UPDATE = 1;
// and so on ...
public static void validate(User user, int useCase) throws Exception{
ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
Validator validator = factory.getValidator();
Set<ConstraintViolation<User>> violations = validator.validate(user);
if (!violations.isEmpty())
throw new Exception("User object failed base validations");
switch (useCase){
case CREATE:
createCase(user);
break;
case UPDATE:
updateCase(user);
break;
}
}
public static void createCase(User user) throws Exception{
//additional validations for this use case
}
public static void updateCase(User user)throws Exception{
//additional validations for this use case
}
// add similar functions for different cases ...
}

服务等级将看起来像-

public class UserService{
public void createUser(User user){
UserValidator.validate(user, UserValidator.CREATE);
//...
}
public void updateUser(User user){
UserValidator.validate(user, UserValidator.UPDATE);
//...
}
} 

相关内容

最新更新