详解Java实现设计模式之责任链模式
一、模拟业务需求
假设我们现在需要在我们的系统中导入一批关于学生信息的Excel的数据,其主要的信息有:学号、姓名、年龄、性别等等,在导入系统的时候,我们肯定不能直接的保存到数据库,我们肯定是先要对这个Excel的数据进行校验,看是否符合系统的要求,只有都符合了系统的要求了,我们把这些数据保存到数据库中去。假如我们的学生对应的实体类如下:
@Data public class Student { /** * 学生编号 */ private String stNo; /** * 学生姓名 */ private String stName; /** * 学生年龄 */ private Integer age; /** * 性别 */ private String gender; }
那么假设我们现在的需求是:在我们的StudentServiceImpl业务实现类里面已经接收到了一个List
二、小步小跑的迭代开发
好,一开始,业务那边的小姑娘小美说这些学生的数据没有什么重要的,只要校验这个学生的姓名不能为空就行且不超过20个字就行了,这个对于你来说就是个小意思,于是你可能在业务代码中先对集合进行遍历然后写下这样的判断:
//判断学生的姓名是否符合条件 if(Objects.nonNull(stu.getStName()) && stu.getStName().length() < 20) { //TODO ...对符合的数据进行下一步的处理 }
过了不久,小美害羞的看着你,对你说:这个年龄也要做判断,必填且不能小于0,不能大于60,改好了请你吃饭。你看着她甜美的笑容,果断的对好说:简单。于是你又加上了这样的代码:
//判断学生的姓名是否符合条件 if(Objects.nonNull(stu.getStName()) && stu.getStName().length() < 20) { if(Objects.nonNull(stu.getStAge()) && stu.getStAge() > 0 && stu.getStAge() < 60) { //TODO ...对符合的数据进行下一步的处理 } }
又过了几天,你又看到小美跑过来,你满怀期待的觉得她是通知你今天下班了一起共进晚餐。但是她却支支吾吾的想说又说不出,这里你心想,完蛋了,会不会共进晚餐的机会泡汤了。这时她说:这个学生的性别也要做校验,且只能是“男”或“女”,加上之前的都要校验通过了才能在到系统里面。听到这里,你不由得松了一口气,共进晚餐的机会还有。于是你说:没问题。于是你又在原先的代码里面进行了迭代:
//判断学生的姓名是否符合条件 if(Objects.nonNull(stu.getStName()) && stu.getStName().length() < 20) { if(Objects.nonNull(stu.getStAge()) && stu.getStAge() > 0 && stu.getStAge() < 60) { if(Object.notNull(stu.getGender()) && ("男".equest(stu.getGender()) || "女".equest(stu.getGender()))) { //TODO ...对符合的数据进行下一步的处理 } } }
你很快的改完了,但是在检查的时候,看着这个代码,总感觉有总说不出来的问题,但是又好像没有什么问题。实然,你想到以后小美肯定还会经常来找你,如果再继续的这样if的写下去,以后越来越难维护了,以后和小美吃饭的时间都没有了。想到这,你不由得直冒冷汗。于是你闭关半天,终于把这个潜在的阻止你和小美吃饭的的拦路虎给解决了。
三、系统对数据的校验要求
- stName(学生姓名):不能为空,不能超过20个字符。
- age(学生年龄):只能为整数,且小于60。
- gender(学生性别):只能是"男"或"女"。
- stNo(学生编号):要求唯一,不能为空,不能超过20个字符,且在数据库中不能已经存在。
四、新建一个抽象类
在这个抽象类中,包含有有一个它自身属性,和一个set方法,此外还要有一个抽象方法,给不同的子类来实现不同的逻辑,详细说明如下:
//抽象的父类 public abstract class AbsCheckStudent { //包含有自身的一个属性,其作业主要是下一个对数据的处理者 protected AbsCheckStudent absCheckStudent; //设定下一个处理者 public void setAbsCheckStudent(AbsCheckStudent absCheckStudent) { this.absCheckStudent = absCheckStudent; } //此方法是业务层调用的方法,即业务调用此方法,把学生的集合做参数传进来即可。 public void handleCheck(List<Student> studentList) { if (Objects.nonNull(studentList) && !studentList.isEmpty()) { List<Student> checkIsOk = checkStudent(studentList); //判断下一个处理者是不是null,即还有没有下一个处理者,且判断数据是否为空 if (Objects.nonNull(absCheckStudent) && Objects.nonNull(checkIsOk) && !checkIsOk.isEmpty()) { //调用下一个处理者的业务处理方法 absCheckStudent.handleCheck(checkIsOk); } } } //此方法是由不同的子类来进行不同的处理实现 public abstract List<Student> checkStudent(List<Student> studentList); }
五、子类的实现
首先实现的是学生姓名的校验的子类
public class StNameCheck extends AbsCheckStudent{ @Override public List<Student> checkStudent(List<Student> studentList) { //获取学生名称不符合条件的学生对象 List<Student> stNameIsNotOk = studentList.stream().filter(stu -> { String stName = stu.getStName(); return Objects.isNull(stName) || "".equals(stName); }).collect(Collectors.toList()); System.out.println("名字校验不通过的数据有:"+ stNameIsNotOk.toString()); //在原有的集合中移除不符合学生姓名的对象集合 studentList.removeAll(stNameIsNotOk); System.out.println("名字校验通过的数据:" + studentList.toString()); //返回通过学生姓名校验的学生的集合 return studentList; } }
然后再实现的是学生年龄的校验子类
public class StAgeCheck extends AbsCheckStudent{ @Override public List<Student> checkStudent(List<Student> studentList) { //获取学生年龄不符合条件的学生对象 List<Student> stAgeIsNotOk = studentList.stream().filter(stu -> { Integer stAge = stu.getAge(); return Objects.isNull(stAge) || stAge <= 0 || stAge >= 60; }).collect(Collectors.toList()); System.out.println("年龄校验不通过的数据有:" + stAgeIsNotOk.toString()); //在原有的集合中移除不符合学生年龄的对象集合 studentList.removeAll(stAgeIsNotOk); System.out.println("年龄校验通过的数据:" + studentList.toString()); //返回通过学生姓名校验的学生的集合 return studentList; } }
最后实现的是学生性别的校验的子类
public class StGenderCheck extends AbsCheckStudent{ @Override public List<Student> checkStudent(List<Student> studentList) { //获取学生年龄不符合条件的学生对象 List<Student> stGenderIsNotOk = studentList.stream().filter(stu -> { String gender = stu.getGender(); return Objects.isNull(gender) || !("男".equals(gender) || "女".equals(gender)); }).collect(Collectors.toList()); System.out.println("性别校验没有通过的数据:" + stGenderIsNotOk.toString()); //在原有的集合中移除不符合学生年龄的对象集合 studentList.removeAll(stGenderIsNotOk); System.out.println("性别校验通过的数据:" + studentList.toString()); //返回通过学生姓名校验的学生的集合 return studentList; } }
六、构建责任链和调用
好了,现在,校验姓名的子类、校验年龄的子类、校验性别的子类都已经实现了。不同职责的子类校验有了,现在我们需要构建一条责任链。即,先通过了姓名校验的数据才能进行下一步的年龄校验,通过了年龄校验的数据才能到性别校验,性别校验通过了,就可以保存数据到数据库了。现在我们构建如下的责任链:
public class Chain { public static AbsCheckStudent getStudentCheck() { //校验姓名 AbsCheckStudent stNameCheck = new StNameCheck(); //校验年龄 AbsCheckStudent stAgeCheck = new StAgeCheck(); //校验性别 AbsCheckStudent stGenderCheck = new StGenderCheck(); //设置好责任链的顺序,把校验年龄的子类当作StNameCheck中的下一个处理者 stNameCheck.setAbsCheckStudent(stAgeCheck); //把校验性别的子类当作StAgeCheck中的下一个处理者 stAgeCheck.setAbsCheckStudent(stGenderCheck); } public static void main(String[] args) { AbsCheckStudent studentCheck = getStudentCheck(); List<Student> studentList = getStudents(); studentCheck.handleCheck(studentList); } public static List<Student> getStudents() { List<Student> result = new ArrayList<>(); Student s1 = new Student(); s1.setAge(12); s1.setGender("男"); s1.setStName("张三"); s1.setStNo(""); Student s2 = new Student(); s2.setAge(12); s2.setGender("男1"); s2.setStName("张三"); s2.setStNo("123"); Student s3 = new Student(); s3.setAge(12); s3.setGender("男"); s3.setStName("张三"); s3.setStNo("123"); result.add(s1); result.add(s2); result.add(s3); return result; } }
最后的运行结果如下:
你看,这样的话,我们只要有有最后校验性别的逻辑里面,对于通过性别校验的数据保存到数据库里面就行了。
七、可维护性
当你闭关出来后,小美又过来找你了,说学生编号要求唯一,不能为空,不能超过20个字符,且在数据库中不能已经存在。只有当编号的校验通过了就可以放心的保存到数据库了。
这时候,你就可以这样进行扩展了,先创建一个子类来继承AbsCheckStudent
public class StGenderCheck extends AbsCheckStudent{ @Override public List<Student> checkStudent(List<Student> studentList) { //获取学生年龄不符合条件的学生对象 List<Student> stNoIsNotOk = studentList.stream().filter(stu -> { String stNo = stu.getStNo(); return Objects.isNull(stNo) || "".equals(stNo) || stNo.length() > 20; }).collect(Collectors.toList()); //TODO 做数据库中的惟一性的校验等 System.out.println("编号校验不通过的数据有:" + stNoIsNotOk.toString()); //在原有的集合中移除不符合学生编号的对象集合 studentList.removeAll(stNoIsNotOk); System.out.println("通过了全部的校验的数据有:" + studentList); //TODO 全部通过校验了,保存数据到数据库 save(studentList); return null; } }
然后我们再在那个责任链上加上这个新的处理节点:
public class Chain { public static AbsCheckStudent getStudentCheck() { //校验姓名 AbsCheckStudent stNameCheck = new StNameCheck(); //校验年龄 AbsCheckStudent stAgeCheck = new StAgeCheck(); //校验性别 AbsCheckStudent stGenderCheck = new StGenderCheck(); //设置好责任链的顺序,把校验年龄的子类当作StNameCheck中的下一个处理者 stNameCheck.setAbsCheckStudent(stAgeCheck); //把校验性别的子类当作StAgeCheck中的下一个处理者 stAgeCheck.setAbsCheckStudent(stGenderCheck); AbsCheckStudent stNoCheck = new StNoCheck(); //把学生的编号校验放到性别校验的后面 stGenderCheck.setAbsCheckStudent(stNoCheck); } // ...... }
运行结果如下:
八、总结
8.1、责任链模式
- 可以控制请求的处理的顺序
- 单一职责原则,可以对发起操作和执行操作的类进行解耦
- 开闭原则,可不用修改原有的业务代码,新增其他的处理类
- 不能保证每个处理者者可以执行
- 效率不是很好,调用时如果不注意会出现各种各样的问题
8.2、责任链模式适用的场景
- 当必须按顺序执行多个处理者时,可以考虑使用责任链模式
- 如果处理者的顺序及其必须在运行时改变时,可以考虑使用责任链模式
以上就是详解Java实现设计模式之责任链模式的详细内容,更多关于Java 设计模式 责任链模式的资料请关注猪先飞其它相关文章!
相关文章
- 这篇文章主要介绍了如何利用java语言实现经典《复杂迷宫》游戏,文中采用了swing技术进行了界面化处理,感兴趣的小伙伴可以动手试一试...2022-02-01
java 运行报错has been compiled by a more recent version of the Java Runtime
java 运行报错has been compiled by a more recent version of the Java Runtime (class file version 54.0)...2021-04-01- 这篇文章主要介绍了在java中获取List集合中最大的日期时间操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2020-08-15
- 这篇文章主要介绍了Java如何发起http请求的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-03-31
- 这篇文章主要介绍了教你怎么用Java获取国家法定节假日,文中有非常详细的代码示例,对正在学习java的小伙伴们有非常好的帮助,需要的朋友可以参考下...2021-04-23
- 说起C#和Java这两门语言(语法,数据类型 等),个人以为,大概有90%以上的相似,甚至可以认为几乎一样。但是在工作中,我也发现了一些细微的差别...2020-06-25
- 这篇文章主要介绍了解决Java处理HTTP请求超时的问题,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2021-03-29
- 神马是“解释器模式”?先翻开《GOF》看看Definition:给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。在开篇之前还是要科普几个概念: 抽象语法树: 解释器模式并未解释如...2014-06-07
- 这篇文章主要介绍了java 判断两个时间段是否重叠的案例,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2020-08-15
- 这篇文章主要介绍了超简洁java实现双色球若干注随机号码生成(实例代码),本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下...2021-04-02
java 画pdf用itext调整表格宽度、自定义各个列宽的方法
这篇文章主要介绍了java 画pdf用itext调整表格宽度、自定义各个列宽的方法,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2021-01-31- 这篇文章主要介绍了Java生成随机姓名、性别和年龄的实现示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2020-10-01
- 这篇文章主要介绍了java正则表达式判断前端参数修改表中另一个字段的值,需要的朋友可以参考下...2021-05-07
Java使用ScriptEngine动态执行代码(附Java几种动态执行代码比较)
这篇文章主要介绍了Java使用ScriptEngine动态执行代码,并且分享Java几种动态执行代码比较,需要的朋友可以参考下...2021-04-15- 这篇文章主要介绍了Java开发实现人机猜拳游戏,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2020-08-03
- 这篇文章主要介绍了Java List集合返回值去掉中括号('[ ]')的操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2020-08-29
Java中lombok的@Builder注解的解析与简单使用详解
这篇文章主要介绍了Java中lombok的@Builder注解的解析与简单使用,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下...2021-01-06Java 8 Stream 的终极技巧——Collectors 功能与操作方法详解
这篇文章主要介绍了Java 8 Stream Collectors 功能与操作方法,结合实例形式详细分析了Java 8 Stream Collectors 功能、操作方法及相关注意事项,需要的朋友可以参考下...2020-05-20- 这篇文章主要介绍了Java线程池中的各个参数如何合理设置操作,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教...2021-06-19
- 下面小编就为大家带来一篇java中String类型变量的赋值问题介绍。小编觉得挺不错的。现在分享给大家,给大家一个参考。...2016-03-28