详解Java实现设计模式之责任链模式
作者:Aj小菜 发布时间:2023-11-08 10:32:07
一、模拟业务需求
假设我们现在需要在我们的系统中导入一批关于学生信息的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、责任链模式适用的场景
当必须按顺序执行多个处理者时,可以考虑使用责任链模式
如果处理者的顺序及其必须在运行时改变时,可以考虑使用责任链模式
来源:https://www.cnblogs.com/byqin/p/14918638.html
猜你喜欢
- 本文实例讲述了Java基于余弦方法实现的计算相似度算法。分享给大家供大家参考,具体如下:(1)余弦相似性通过测量两个向量之间的角的余弦值来度
- 概述从今天开始, 小白我将带大家开启 Java 数据结构 & 算法的新篇章.优先队列优先队列 (Priority Queue) 和队
- 一、常规形式1 项目结构2 配置文件及环境设置(1)配置文件# 应用服务 WEB 访问端口server.port=8080# spring
- 双向循环链表定义相比于单链表,有两个指针,next指针指向下一个结点,prior指针指向上一个结点,最后一个结点的next指针指向头结点,头
- static关键字static关键词与对象无关。static关键字主要修饰四个部分的内容这里我们主要介绍static修饰属性和修饰方法。1.
- 一、简介在Spring中,有这么2个接口:BeanFactory和FactoryBean,名字很相似,很多小伙伴经常混淆,在面试的时候也经常
- 1.申请测试号,并记录appID和appsecret2.关注测试号3.添加消息模板{{topic.DATA}} 用户名: {{user.DA
- 所谓回调,就是客户程序C调用服务程序S中的某个方法A,然后S又在某个时候反过来调用C中的某个方法B,对于C来说,这个B便叫做回调方法。下面看
- 若代理类在程序运行前就已经存在,那么这种代理方式被成为 静态代理 ,这种情况下的代理类通常都是我们在Java代码中定义的。 通常情况下, 静
- 题目题目要求思路:模拟用一个哈希表记录可出现的字母,然后逐一遍历每个单词每个字母,符合条件则结果加一。Javaclass Solution
- 一,项目简介经过调查研究进行开发设计的这款仓库管理系统,主要是为商家提供商品货物进销存的信息化管理,以便让商家在竞争如此激烈的今天占据一定的
- 本文实例为大家分享了Android登录注册功能的具体代码,供大家参考,具体内容如下展示效果代码区MainActivity(登录方法)publ
- Java注解(annotation)简单上手反射reflect:https://www.jb51.net/article/221282.ht
- 鼠标事件监听机制的三个方面:1.事件源对象:事件源对象就是能够产生动作的对象。在Java语言中所有的容器组件和元素组件都是事件监听中的事件源
- 本文实例为大家分享了flutter实现倒计时加载页面的具体代码,供大家参考,具体内容如下效果图实现步骤1、pubspec.yaml中添加依赖
- 一、原理1、不变模式(不可变对象)在并行软件开发过程中,同步操作似乎是必不可少的。当多线程对同一个对象进行读写操作时,为了保证对象数据的一致
- 一、注解(annotations)列表 @SpringBootApplication:包含了@ComponentScan、@Configur
- 1、String类1.1两种对象实例化方式对于String在之前已经学习过了基本使用,就是表示字符串,那么当时使用的形式采取了直接赋值:pu
- 这个工具比较简单,用于配合另外一个工具进行文件传送,废话少说,上代码import java.net.URL;import java.net.
- spring注入枚举类型作为参数//定义枚举类型public enum ReportType { MONTH,WE