首页 > 解决方案 > 使用 java 8 创建验证框架

问题描述

我正在用 java 8 开发一个伪验证框架。

到目前为止,我所拥有的是这个接口,它是 Java 中 Predicate 接口的重写:

@FunctionalInterface
public interface Validation<K> {

public GenericValidationResult test(K param);

default Validation<K> and(Validation<K> other) {
return (param) -> {
  GenericValidationResult result = this.test(param);
  return !result.isValid() ? result : other.test(param);
};
}

default Validation<K> or(Validation<K> other) {
return (param) -> {
  GenericValidationResult result = this.test(param);
  return result.isValid() ? result : other.test(param);
};
}

该接口由下面的具体实现实现:

public class GenericValidation<K> implements Validation<K> {

private Predicate<K> predicate;

public static <K> GenericValidation<K> from(Predicate<K> predicate) {
  return new GenericValidation<K>(predicate);
}

private GenericValidation(Predicate<K> predicate) {
  this.predicate = predicate;
}

@Override
public GenericValidationResult test(K param) {
  return predicate.test(param) ? GenericValidationResult.ok() :     GenericValidationResult.fail();
}

这个的职责是执行某个条件的谓词并创建一个由 GenericValidationResult 表示的结果:

public class GenericValidationResult {

private boolean valid;

public boolean isValid() {
  return valid;
}

public static GenericValidationResult ok() {
  return new GenericValidationResult(true);
}

private GenericValidationResult(boolean valid) {
  this.valid = valid;
}

public static GenericValidationResult fail() {
  return new GenericValidationResult(false);
}

public Optional<String> getFieldNameIfInvalid(String field) {
  return this.valid ? Optional.empty() : Optional.of(field);
}

这就是我的框架的基础。我创建了这个类:

public class ValidatorUtil {                                                                                            

public static final Validation<String> notNullString = GenericValidation.from(s -> s != null);                        

public static final Validation<Object> notNullObject = GenericValidation.from(s -> s != null);                        

public static final Validation<String> notEmptyString = GenericValidation.from(s -> !s.isEmpty());                    

public static final Validation<Integer> notNullInteger = GenericValidation.from(s -> s != null);                      

public static final Validation<Integer> greaterThanZero = GenericValidation.from(s -> s > 0);                         

public static final Validation<List> notEmptyList = GenericValidation.from(s -> s != null && !s.isEmpty());           

public static final Validation<Enum> notNullEnum = GenericValidation.from(s -> s != null);                            

public static final Validation<String> stringMoreThan(int size) {                                                     
  return GenericValidation.from(s -> ((String) s).length() > size);                                                   
};                                                                                                                    

public static final Validation<String> stringLessThan(int size) {                                                     
  return GenericValidation.from(s -> ((String) s).length() < size);                                                   
};                                                                                                                    

public static final Validation<String> stringBetween(int morethan, int lessThan) {                                    
  return stringMoreThan(morethan).and(stringLessThan(lessThan));                                                      
};                                                                                                                    

public static final Validation<Integer> integerMoreThan(int limit) {                                                  
  return GenericValidation.from(s -> s > limit);                                                                      
};                                                                                                                    

public static final Validation<Integer> integerLessThan(int size) {                                                   
  return GenericValidation.from(s -> s < size);                                                                       
};                                                                                                                    

public static final Validation<Integer> integerBetween(int morethan, int lessThan) {                                  
  return integerMoreThan(morethan).and(integerLessThan(lessThan));                                                    
};                                                                                                                    

这是我的框架目前支持的所有验证。我现在要做的是创建一个通用验证。基本上我已经创建了这个名为 dummy 的对象:

public class Dummy {

private String firstName;
private String lastName;
private String email;
private Integer age;

public Dummy() {
}

public String getFirstName() {
  return firstName;
}

public void setFirstName(String firstName) {
  this.firstName = firstName;
}

public String getLastName() {
  return lastName;
}

public void setLastName(String lastName) {
  this.lastName = lastName;
}

public String getEmail() {
  return email;
}

public void setEmail(String email) {
  this.email = email;
}

public Integer getAge() {
  return age;
}

public void setAge(Integer age) {
  this.age = age;
}

}

想象一下,这个对象只有在假人的名字以 S 开头,姓氏以 P 开头且年龄大于 20 时才有效。目前我无法使用我的框架执行此验证,我尝试创建此验证但它没有工作:

public static final <T> Validation<T> validateObject(Predicate<T> p) {
 return GenericValidation.from(p);                                  
}                                                                     

并使用如下所示的相同方法:

private static boolean test(Employee e) {
  return ValidatorUtil.validateObject(em ->   em.getFirstName().startsWith("S") && em.getLastName().startsWith("P") && em.getAge() > 20).test(e);
}

public static void main(String[] args) {
    Employee e = new Employee();
    e.setAge(123);
    e.setEmail("email");
    e.setFirstName("firstName");
    e.setLastName("lastName");

    test(e);
}

我想让这个方法抽象,因为我不想只在 Dummy 对象上使用它,如果将来我创建一个 Car 对象,我希望能够验证同一个汽车对象。

我应该怎么办?

标签: javajava-8predicate

解决方案


Java 8 中的通用实用程序

编写一个类 AbstractValidator

public abstract class AbstractValidator {

private Map<Predicate, String> validatorMap = new HashMap<>();
protected List<String> messages;

public AbstractValidator() {
    this.messages = new ArrayList<>();
}

protected <E> AbstractValidator add(Predicate<E> predicate, String reason) {
    validatorMap.put(predicate, reason);
    return this;
}

protected AbstractValidator apply(String fieldName, Object val) {
    this.validatorMap.forEach((modifier, reason) -> {
        if (!modifier.test(val)) {
            String message = MessageFormat.format("{0} {1}", fieldName, reason);
            messages.add(message);
        }
    });
    this.validatorMap.clear();
    return this;
}



public void end() {
    Optional.ofNullable(messages).filter(CollectionUtils::isEmpty)
            .orElseThrow(() -> {
                RuntimeException businessException = new RuntimeException( messages);
                messages.clear();
                return businessException;
            });
}

实现 AbstractValidator 的 GenericValidator 类

public class GenericValidator extends AbstractValidator {

private GenericValidator() {
    super();
}

public static GenericValidator of() {
    return new GenericValidator();
}

public GenericValidator nonNull() {
    add(Objects::nonNull, "Field value is null");
    return this;
}

public GenericValidator isNotEmpty() {
    add(StringUtils::isNotEmpty, "Field is empty");
    return this;
}

public GenericValidator min(int min) {
    add(s -> ((String) s).length() >= min, "Field min size is " + min);
    return this;
}

public GenericValidator max(int max) {
    add(s -> ((String) s).length() <= max, "Field max size is " + max);
    return this;
}

public GenericValidator apply(String fieldName, Object val) {
    return (GenericValidator) super.apply(fieldName, val);
}

junit 测试用例

class GenericValidatorTest {

@Test
void genericValidation() {
    Fulfillment input = Fulfillment.builder().fulfillmentId(UUID.randomUUID().toString()).fulfillmentStatus(FulfillmentStatus.CREATED)
            .build();
    GenericValidator of = GenericValidator.of();
    of.nonNull().apply("fulfillment", input).end();
    of.nonNull().apply("fulfillmentStatus", input.getFulfillmentStatus())
    .isNotEmpty().min(36).max(36).apply("fulfillmentId", input.getFulfillmentId()).end();
}

@Test
void genericValidationWithExceptionInput() {
    Fulfillment input = Fulfillment.builder().fulfillmentId(UUID.randomUUID().toString()).fulfillmentStatus(FulfillmentStatus.CANCELLED)
            .build();
    GenericValidator validator = GenericValidator.of()
            .nonNull().apply("fulfillment", input)
            .nonNull().apply("fulfillmentStatus", input.getFulfillmentStatus())
            .isNotEmpty().min(37).max(35).apply("fulfillmentId", input.getFulfillmentId());
    Assert.assertThrows(RuntimeException.class, () -> validator.end());


}

推荐阅读