java - 使用 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 对象,我希望能够验证同一个汽车对象。
我应该怎么办?
解决方案
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());
}
推荐阅读
- python - 如何将墨卡托投影的方形窗口转换为圆形?
- reactjs - 从 Safari 上传图像并在 Web 应用程序中转换为 webp
- python - 使用 .loc 从列表中过滤元素
- gis - 如何参考原点在 ArcMap 中绘制笛卡尔坐标?
- php - 如何在 Laravel 的搜索表单中使用分页
- android - 是否能够访问 android 11 系统应用程序,示例消息
- django - 在 Django Rest Framework 中测试视图集的自定义操作
- python - 如何创建像列一样的子类化熊猫数据框的属性
- python - 在 subprocess.run(..., check=True) 内部,如何防止失败的命令以非零状态退出?
- javascript - 具有范围滑块角度的自定义进度条