为了账号安全,请及时绑定邮箱和手机立即绑定

Java 8 Lambda 表达式验证

Java 8 Lambda 表达式验证

三国纷争 2021-12-01 19:45:22
我在此处阅读有关使用谓词进行验证的文章。我正在尝试在 Spring Boot 框架中实现它,但我遇到了一些问题。在代码中:public class LamdaPersonValidator implements PersonValidator {   public void validate(Person person) {     notNull.and(between(2, 12)).test(person.getFirstName()).throwIfInvalid("firstname");     notNull.and(between(4, 30)).test(person.getLastName()).throwIfInvalid("secondname");     notNull.and(between(3, 50)).and(contains("@")).test(person.getEmail()).throwIfInvalid("email");     intBetween(0, 110).test(person.getAge()).throwIfInvalid("age");   } }没有提到检查验证方法中的人对象本身是否为空的标准方法。是否可以只进行空检查,if(persone != null) { // notNull.and..}或者可能有一些更好的方法来进行空检查。另一件事是假设,我想做一些自定义检查,例如数据库中是否存在人员。在这种情况下,我需要连接到数据库进行检查。在这种情况下,我需要自动装配无法使用静态变量和方法的接口。那么,从数据库进行验证时,使用它的最佳方法是什么?
查看完整描述

2 回答

?
鸿蒙传说

TA贡献1865条经验 获得超7个赞

我们不是神圣裁判所的代码法官,所以我们没有责任告诉你,“只做一个空检查就可以”。


当然,可以将 is 写成普通if语句,就像我们过去 25 年所做的那样,就像可以发明一个详细的框架来封装null检查并以某种方式将术语“lambda”带入其中。剩下的唯一问题是你是否真的打算写作if(person != null) { /* do the checks */ },换句话说,让一个null人通过考试。


如果您想拒绝null人员(这会更合理),已经有可能在没有显式测试的情况下编写它Objects.requireNonNull,因为 Java 7,这表明您不需要“一切都变得更好”框架以实现该目标。一般来说,你可以用常规代码合理地编写验证代码,与文章示例相反,使用&&运算符等简单工具,将常用代码放入方法中:


public void validate(Person person) {

    Objects.requireNonNull(person, "person is null");

    checkString(person.getFirstName(), "first name", 2, 12);

    checkString(person.getLastName(), "last name", 4, 30);

    checkString(person.getEmail(), "email", 3, 50);

    if(!person.getEmail().contains("@"))

        throw new IllegalArgumentException("invalid email format");

    checkBounds(person.getAge(), "age", 0, 110);

}

private void checkString(String nameValue, String nameType, int min, int max) {

    Objects.requireNonNull(nameValue, () -> nameType+" is null");

    checkBounds(nameValue.length(), nameType, min, max);

}

private void checkBounds(int value, String valueType, int min, int max) {

    if(value < min || value > max)

        throw new IllegalArgumentException(valueType+" is not within ["+min+" "+max+']');

}

这与您的代码相同,没有任何名称中带有“Lambda”的框架,仍然具有可读的验证代码并允许重用检查代码。也就是说LamdaPersonValidator,您应该使用反映类职责的类名,而不是反映您如何实现它的类名。显然,负责验证对象某些属性的验证器不应与检查数据库中实体存在的验证器混淆。后者本身就是一个完全不同的话题,也应该是一个单独的问题。


上面的代码只是一个例子,如何实现与原始代码相同。它永远不应该以这种形式出现在生产代码中,因为它展示了一种广泛存在的反模式,将任意不合理的约束应用于属性,很可能是程序员在编写代码时发明的。


为什么它假设一个人必须有名字和姓氏,为什么它假设名字必须至少有两个到十二个字符,而姓氏必须在四到三十个字符之间?


它实际上甚至不是字符,因为char单位和实际字符之间的关联不是 1:1。


对于每个考虑实现名称验证的程序员来说,必须阅读Falsehoods Programmers Being About Names (With Examples)。


同样,维基百科经验证的最年长者名单列出了 100 名 110 岁以上的人。


并且没有理由假设电子邮件地址不能超过五十个字符。一个正确的电子邮件模式的真正的验证可能会变成被什么东西故意省略......


查看完整回答
反对 回复 2021-12-01
?
慕的地10843

TA贡献1785条经验 获得超8个赞

您也可以像这样编写 GenericValidator:


编写AbstractValidator普通工作类:


public abstract class AbstractValidator {


    private Map<Predicate, String> validatorMap = new LinkedHashMap<>();

    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) {

        AtomicBoolean flag= new AtomicBoolean(true);

        this.validatorMap.forEach((modifier, reason) -> {

            if (flag.get() && !modifier.test(val)) {

                String message = MessageFormat.format("{0} {1}", fieldName, reason);

                messages.add(message);

                flag.set(false);

            }

        });

        this.validatorMap.clear();

        return this;

    }


    public void end(String exceptionStatus) {

        Optional.ofNullable(messages).filter(CollectionUtils::isEmpty)

                .orElseThrow(() -> {

                    RuntimeException ex = new RuntimeException(exceptionStatus, messages);

                    messages.clear();

                    return ex;

                });

    }

}

编写GenericValidator将扩展AbstractValidator验证实现的类:


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 notEmpty() {

        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 notEmptyList() {

        add(CollectionUtils::isNotEmpty, "Field List is null/Empty");

        return this;

    }


    public GenericValidator apply(String fieldName, Object val) {

        return (GenericValidator) super.apply(fieldName, val);

    }

}

请相应地进行测试。测试用例示例:


class GenericValidatorTest {


    @Test

    void genericValidationSuccessCase() {

        Abc abc = new Abc();

        abc.setName("a");

        abc.setVal(1);

        abc.setAbslist(Collections.singletonList(new ChildAbc()));

        GenericValidator of = GenericValidator.of();

        of.nonNull().apply("abc", abc).end(GENERIC_JSON_SERIALIZATION);

        of.notEmpty().min(1).max(1).apply("name", abc.getName())

                .nonNull().apply("value", abc.getVal())

                .notEmptyList().apply("childAbc", abc.getAbslist())

                .end(GENERIC_JSON_SERIALIZATION);

    }


    @Test

    void genericValidationWhenObjectNull() {

        GenericValidator of = GenericValidator.of();

        Assertions.assertThrows(BusinessException.class, () -> of.nonNull()

                .apply("abc", null).end(GENERIC_JSON_SERIALIZATION));

    }


    @Test

    void genericValidationWithExceptionInput() {

        Abc abc = new Abc();

        abc.setName("a");

        abc.setVal(1);

        GenericValidator of = GenericValidator.of();

        of.nonNull().apply("abc", abc).end(GENERIC_JSON_SERIALIZATION);

        GenericValidator apply = of.notEmpty().min(1).max(1).apply("name", abc.getName())

                .nonNull().apply("value", abc.getVal())

                .notEmptyList().apply("childAbc", abc.getAbslist());

        Assertions.assertThrows(BusinessException.class, () -> apply.end(GENERIC_JSON_SERIALIZATION));


    }

}


class Abc {


    String name;

    Integer val;

    List<ChildAbc> abslist;


    public String getName() {

        return name;

    }


    public void setName(String name) {

        this.name = name;

    }


    public Integer getVal() {

        return val;

    }


    public void setVal(Integer val) {

        this.val = val;

    }


    public List<ChildAbc> getAbslist() {

        return abslist;

    }


    public void setAbslist(List<ChildAbc> abslist) {

        this.abslist = abslist;

    }

}


class ChildAbc {


    String name;


    public String getName() {

        return name;

    }


    public void setName(String name) {

        this.name = name;

    }

}


查看完整回答
反对 回复 2021-12-01
  • 2 回答
  • 0 关注
  • 169 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信