点击上方蓝字关注我!
背景
假设我们要开发一个创建超级俱乐部会员的功能,要求创建的条件为:
“
会员的id要求为【1-10】 电话号码要求为185开头 注册日期不能小于5月15日 ”
通常情况下我们会这么写:
public void creatSupperClubMember(Member member) {
//1.校验会员id
//2.校验电话号码
//3.校验注册日期
//4.创建超级俱乐部会员
}
上面这个方法中,我们发现除了超级会员的创建,我们有一大半的代码是在校验参数。这样有个问题就是我们正真需要的代码是4,从某种程度上来说1、2、3是多余的代码。
如果我们的校验非常复杂,那么这个方法就会显得难以阅读,通过分析我们发现这个方法其实做了两件事
“
校验参数 创建超级会员 ”
因此我们将代码重构下:
public void creatSupperClubMember(Member member) {
//1.参数校验
validatorBeforeCreate()
//2.创建超级俱乐部会员
}
public void validatorBeforeCreate() {
//1.校验会员id
//2.校验电话号码
//3.校验注册日期
}
这样我们的代码会显得更加清晰。事实上,我们在编写方法时,需要考虑单一职责原则,业务的参数校验从某种程度上来说属于非业务代码,上面的功能我们可以抽象出:
没错,就是业务逻辑与非业务逻辑,我们有没有方法可以将业务逻辑与非业务逻辑解耦呢。
我们可以使用注解校验。
其实我们在平时的开发中,很多地方都是用了注解的校验:
上面的代码相信大家都写过,我们不需要在方法中去写参数的校验,我们在字段上使用注解,就是实现了参数的必填校验,范围校验。
但是已有的注解无法满足我们的要求,实际的参数校验比较复杂。因此我决定自己写一个参数校验的注解。
注解模型
我想要的需求是
@ValidatorHandler(validators = XXXXValidator.class)
public int createXXX(XXX xxx) {
}
我们在写的业务代码上面添加一个@ValidatorHandler
注解,注解里面的XXXXValidator
类就是真正写校验功能的类,会把业务参数传到这个校验类中。
public class XXXValidator extends AbstractValidator {
@Override
public void check(Object o) {
//获取参数
XXXx xxx = (XXX)o;
//相关的业务校验
}
}
校验类模型如上所示。
我们通过注解将业务代码与校验代码分开。
编写校验注解
首先我们来定义注解:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ValidatorHandler {
/**
* 校验的类
*/
Class<?> validators();
}
该注解作用与方法上,里面的参数validators
为校验类的class。
然后编写实现校验注解的功能
为了清晰展现代码,我用图片表示。
上述代码实现了几个功能:
“
实例化
validators
对应的校验类获取业务参数,并将参数传递到校验类中的check方法的参数中
执行校验类中的check方法
执行业务代码
”
为了规范校验类编写,我们需要定义一个接口
public interface AbstractValidator {
void check(Object o);
}
所有的校验类都要是AbstractValidator
的实现,并实现check
方法。
这里的注解实现功能中,我只获取了业务功能中第一个参数,也就是说我们的业务方法的第一个参数会被校验,大家可以思考下:如果业务功能的参数有多个,该注解的功能类怎么编写?
代码测试
以上我们的注解就开发好了,我们开始测试下功能:
首先我们编写校验类:
如上图,校验类中我们实现了文章开头要求的三个校验功能。
然后我们编写业务代码,创建超级俱乐部会员
@ValidatorHandler(validators = MemberValidator.class)
public void creatSupperClubMember(Member member) {
System.out.println("member开始新增");
//这里就是我们的业务代码
System.out.println("member新增结束");
}
我们再写一个方法,模拟前端传入的数据:
测试方法如下:
@Test
public void testCreateMember() {
Member member = initData();
testMemberService.creatSupperClubMember(member);
System.out.println("创建结束");
}
校验id
private Member initData() {
Member member = new Member();
member.setId("11");
member.setMobile("17790990033");
member.setCreateBy("李老师");
member.setCreateDate(new Date());
member.setIsActive("Y");
return member;
}
执行测试方法,发现控制台打印如下,我们发现id = 11不符合要求,校验成功。
我们修改id = 5,id的校验通过。
member.setId("5");
校验电话号码moblie
然后我们再次执行发现手机号不是185开头,这里的校验也成功。
修改手机号为185的,校验通过。
member.setMobile("18590990033");
校验注册日期
member的参数中,注册日期为:2021-05-15小于2021-05-20,不满足要求。
private Member initData() {
String registerDateStr = "2021-05-15";
Date registerDate = DateUtil.parse(registerDateStr);
Member member = new Member();
member.setId("5");
member.setMobile("18590990033");
member.setCreateBy("李老师");
member.setCreateDate(registerDate);
member.setIsActive("Y");
return member;
}
控制台会出现如下错误,校验成功。
校验全部通过
private Member initData() {
Member member = new Member();
member.setId("5");
member.setMobile("18590990033");
member.setCreateBy("李老师");
member.setCreateDate(new Date());
member.setIsActive("Y");
return member;
}
当我们的参数全部满足业务校验需求时,我们的校验通过,就会创建超级俱乐部会员。
以上就是这篇文章的全部内容了,当我们的业务逻辑校验很复杂时,我们可以使用上面的校验注解将校验逻辑与业务逻辑分开,这样有利于业务与非业务解耦,也满足设计原则的单一职责原则。除了方便阅读,还有的好处就是,当我们不需要校验时,我们可以将业务方法上的校验注解注释掉,这样我们就不必在业务代码中去修改了,从而减少了因修改业务代码导致bug的风险。
如果文章对你有用,欢迎点赞转发😀。
往期推荐
扫码二维码
获取更多精彩
Lvshen_9
原文始发于微信公众号(Lvshen的技术小屋):我用自定义注解优雅的实现了业务的复杂校验
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/261924.html