实战—责任链模式

责任链


最近在开发一个比较复杂的系统,系统中目前写到了工作流(工作流是公司内架构师设计)模块。

某一工作流中针对一个对象的属性、参数校验,而不同的工作流可冗余了其中的某些校验代码。

对象的单独特征的校验在不同的工作流中存在复用,使用责任链模式将各个校验器进行单独开发,然后链起来,以达到高复用的目的。

正文

设计模式在我们日常的开发中是非常热门的一项话题。 开发不是按照设计模式进行死板的开发,而是开发出来后,根据实际场景进行迭代,迭代后发现其满足某项设计模式。 所以别想着死套设计模式到你的代码之中,而是去思考,什么才是最适合自己业务的,这样才能构建一个优美的系统。

本文描述的责任链模式与菜鸟教程之类的有所不同,是针对于系统中要对某些对象进行校验的一个校验责任链。

场景描述

开始之前我们先讲述一下我的设计场景

如果你要出去放风筝,首先你要考虑如下因素:

  1. 天气如何
  2. 今天有空吗
  3. 出门工具
  4. 风筝还在吗
  5. 风筝还具备飞行能力吗
  6. 时间方便吗

当然,除了放风筝这件事,你发现你干其他事情的时候,都需要考虑其中的某些因素,比如工作日要考虑天气和出门工具等等。 所以我们将这些因素设置为单独的校验器。然后创建一个链条(事件),每次就可以直接去执行这个联调里面的校验事项。

我们设定一个检验接口 checkHandler ,下面有不同的实现:天气校验、空闲校验、对象存在校验等 接口方法:check(T data);

在这里插入图片描述

创建校验器之后,我们再创建一个责任链类,用于存放这些校验器。

在这里插入图片描述

针对与不同的事件就是不同的责任链,我们只需要顺序放入校验器,执行校验器的校验方法doChain(T), dochain(T) 中,是顺序遍历 LinkedList-chain 执行其中的校验方法。

创建一个放风筝Chain:

// 伪代码
CheckChain<风筝> chain = new CheckChain();
chain.addHandlers(new 空闲校验(), new 天气校验(), 风筝存在校验(), new出行工具校验());

在这里插入图片描述

然后运行这个chain

// 伪代码
chain.doChain(风筝);

当然,你在其他事务中可能也会宠用其中的空闲、天气、出行工具校验,直接套进去即可。 这样就完成了一个实际应用版的责任链模式。

实战

我们再写一个实例程序,用来校验User。

1 依赖

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.4</version>
</dependency>

2 校验流程

  1. 用户是否存在,其基本属性是否存在
  2. 性别校验 (0:女 1:男)
  3. 年龄校验 (0 - 200)

3 实体

首先创建User类,其中有三个属性:

  1. 姓名
  2. 性别
  3. 年龄
@Getter
public class User {

	private final String name;
	private final Integer age;
	private final Integer sex;

	public User(String name, Integer age, Integer sex) {
		this.name = name;
		this.age = age;
		this.sex = sex;
	}

	@Override
	public String toString() {
		return "User{" +
				"name='" + name + '\'' +
				", age=" + age +
				", sex=" + sex +
				'}';
	}
}

然后定义校验器接口 <<interface>> CheckHandler

4 检验器接口

public interface CheckHandler<T> {

	/**
	 * 校验数据
	 * @param data 数据
	 */
	public void check(T data);
}

所有的校验器都需要实现方法:check(T data)

这里创建三个实现类

在这里插入图片描述

校验器-用户属性非空验证

@Slf4j
public class UserParamNotNullCheck<T extends User> implements CheckHandler<T> {

	@Override
	public void check(User user) {
		if (null == user || null == user.getSex() || null == user.getAge() || null == user.getName()) {
			throw new RuntimeException("xxx参数为空");
		} else {
			log.info("非空验证通过, {}", user);
		}
	}
}

校验器-用户性别

@Slf4j
public class UserSexCheck<T extends User> implements CheckHandler<T> {
	@Override
	public void check(User user) {
		Integer sex = user.getSex();
		if (0 > sex || sex > 1) {
			throw new RuntimeException("性别不存在:" + sex);
		} else {
			log.info("性别验证通过, {} 性", user.getSex() == 1 ? "男" : "女");
		}
	}
}

校验器-用户年龄

@Slf4j
public class UserAgeCheck<T extends User> implements CheckHandler<T> {

	private final Integer MIN_AGE = 0;
	private final Integer MAX_AGE = 200;

	@Override
	public void check(User user) {
		Integer age = user.getAge();
		if (MIN_AGE > age || MAX_AGE < age) {
			throw new RuntimeException("年龄异常: " + age);
		} else {
			log.info("年龄验证通过, {} 岁", user.getAge());
		}
	}
}

5 责任链类-存储校验器

在这里插入图片描述

创建责任链之后,往里面塞入需要检验的类,最后调用责任链的doChain(Data) ,就会依次执行责任链里面的校验方法。

public class CheckChain<T> {

	private final LinkedList<CheckHandler<T>> handlerChain;

	public CheckChain() {
		this.handlerChain = new LinkedList<>();
	}

	public void addHandler(CheckHandler<T> handler) {
		handlerChain.add(handler);
	}

	@SafeVarargs
	public final void addHandlers(CheckHandler<T>...handlers) {
		Arrays.stream(handlers).sequential().forEach(this::addHandler);
	}

	/**
	 * 执行责任链中的校验器的校验
	 * @param t
	 */
	public void doChain(T t) {
		while (handlerChain.size() > 0) {
			CheckHandler<T> tCheckHandler = handlerChain.pollFirst();
			tCheckHandler.check(t);
		}
	}
}

6 测试

public class ExecutorMain {
	public static void main(String[] args) {
		CheckChain<User> checkChain = new CheckChain<>();
		checkChain.addHandlers(new UserParamNotNullCheck<>(), new UserSexCheck<>(), new UserAgeCheck<>());
		checkChain.doChain(new User("Dcpnet", 22, 1));
	}
}

/**
output:
    15:03:18.490 [main] INFO cn.dcpnet.springstudy.check.UserParamNotNullCheck - 非空验证通过, User{name='Dcpnet', age=22, sex=1}
    15:03:18.492 [main] INFO cn.dcpnet.springstudy.check.UserSexCheck - 性别验证通过, 男 性
    15:03:18.492 [main] INFO cn.dcpnet.springstudy.check.UserAgeCheck - 年龄验证通过, 22 岁
*/

不同于菜鸟教程中,其实很多地方的责任链模式也是如同这样使用的一个list去持有的链,所以说不要套着设计模式开发,要根据实际业务进行设计模式。

加油,奋斗中的少年,在未来的光明之中必有你的一席之地,向光而行。

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值