Java Combinator Pattern注册校验中apply方法是否重复调用问题
参考代码
public interface CustomerRegVali extends Function<Customer, ValidationResult> { static CustomerRegVali isEmailValid(){ return customer -> customer.getEmail().contains("@") ? SUCCESS : EMAIL_NOT_VALID; } static CustomerRegVali isDobValid(){ return customer -> Period.between(customer.getDob(), LocalDate.now()).getYears() > 16 ? SUCCESS : NOT_ADULT; } static CustomerRegVali isPhoneValid(){ return customer -> customer.getPhone().startsWith("+0") ? SUCCESS : PHONE_NOT_VALID; } default CustomerRegVali and(CustomerRegVali other){ return customer -> { ValidationResult result = CustomerRegVali.this.apply(customer); return result.equals(SUCCESS) ? other.apply(customer) : result; }; } } // 主方法调用逻辑 ValidationResult result = isEmailValid() .and(isPhoneValid()) .and(isDobValid()) .apply(customer);
结论
不存在冗余调用,每个基础校验的apply方法最多只会执行1次
执行逻辑说明
你实现的and组合方法是标准的短路逻辑,整个调用流程分为两个阶段:
- 规则组装阶段:
isEmailValid().and(isPhoneValid()).and(isDobValid())这段链式调用仅完成校验规则的包装拼接,不会触发任何校验逻辑,也不会调用任何apply方法。 - 实际校验阶段:只有最后调用
apply(customer)时才会逐层执行校验:- 先执行
isEmailValid的校验逻辑,失败直接返回对应错误,后续校验完全不会触发 - 邮箱校验通过才会执行
isPhoneValid的校验逻辑,失败直接返回,不会触发年龄校验 - 手机号校验通过才会执行
isDobValid的校验逻辑
- 先执行
实现优势
这个组合器模式的实现是非常规范的:
- 校验规则和组合逻辑完全解耦,新增校验规则仅需新增对应静态方法即可
- 短路逻辑可以避免不必要的计算,性能表现优秀
- 链式调用的写法可读性极强,校验顺序一目了然
内容的提问来源于stack exchange,提问作者Rasmus Hjorth Lüdeking
相关产品推荐
相关产品推荐

