多重校验神器责任链模式
Posted xhmj12
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了多重校验神器责任链模式相关的知识,希望对你有一定的参考价值。
背景
最近在做需求,写一个方法,先在前面做验证,if 不满足 A 条件则 return,if 不满足 B 条件则 return...一共写了 5 个验证,等验证通过以后才执行下面的逻辑,这个也没问题。过了一阵产品提了需求,跟这个方法类似,我又把这个方法 copy 了一份,只不过验证条件稍微有点不一样,要变成 6 个验证了。
这时候我就发现有三个问题,第一重复代码,相同的 A 条件 B 条件 C 条件写了两份,没有复用。第二,“头重脚轻”,比如 100 行的方法,前面 60 行都是验证,后面 40 行才是真正有用的业务代码,你看一个方法功能的时候前面验证肯定是不关心的,只看后面 40 行到底在干什么逻辑,所以要缩短验证代码的行数。第三,先后顺序,一个方法A是在B之前验证的,另一个方法 A 是在 B 之后验证的,调整起来很不方便。
这时候我就想到了用「责任链模式」来进行优化解决。
定义
责任链模式(Chain of Responsibility Pattern)是将链中每一个节点看作是一个对象,每个节点处理的请求均不同,且内部自动维护一个下一节点对象。当一个请求从链式的首端发出时,会沿着链的路径依次传递给每一个节点对象,直至有对象处理这个请求为止。属于行为型模式。
生活中的应用场景就是「审批流」。责任链模式主要是解耦了请求与处理,客户只需将请求发送到链上即可,无需关心请求的具体内容和处理细节,请求会自动进行传递直至有节点对象进行处理。
通用UML类图
例子
下面写一个登录验证判断的例子,一般责任链模式会搭配着「建造者模式」一起用,即「链式编程」。因为这样链条看起来更加清晰明了,而传统的写法很抽象,很难看出谁谁谁在谁的前面,谁谁谁在谁的后面,如下所示:
AAAHandler.setNextHandler(deptManagerLeaveHandler);
directLeaderLeaveHandler.setNextHandler(deptManagerLeaveHandler);
BBBHandler.setNextHandler(AAAHandler);
deptManagerLeaveHandler.setNextHandler(gManagerLeaveHandler);
下面先创建一个 Handler 的抽象类,这个类里面有一个下一个 Handler 处理器 next,还有一个 Builder,这个就是用来构建链的,也是方便我们的链式编程。
public abstract class Handler<T>
protected Handler next;
private void next(Handler next)
this.next = next;
public abstract void doHandler(Member member);
public static class Builder<T>
private Handler<T> head;
private Handler<T> tail;
public Builder<T> addHandler(Handler handler)
if (this.head == null)
this.head = this.tail = handler;
return this;
this.tail.next(handler);
this.tail = handler;
return this;
public Handler<T> build()
return this.head;
下面写非空校验 ValidateHandler 类,这里面先判断用户名和密码是否为空,空的话返回,非空的话判断 next 是否为空,非空的话就丢给下一个处理器去执行。
public class ValidateHandler extends Handler
@Override
public void doHandler(Member member)
if (StringUtils.isEmpty(member.getUsername()) ||
StringUtils.isEmpty(member.getPassword()))
System.out.println("用户名和密码不能为空");
return;
if (null != next)
next.doHandler(member);
创建登录检验LoginHandler类,判断账号密码是否正确
public class LoginHandler extends Handler
@Override
public void doHandler(Member member)
if (!"jack".equals(member.getUsername()) || !"666".equals(member.getPassword()))
System.out.println("用户名密码不正确");
return;
if (null != next)
next.doHandler(member);
创建权限检验 AuthHandler 类,判断角色是否有权限
public class AuthHandler extends Handler
@Override
public void doHandler(Member member)
if (!"管理员".equals(member.getRoleName()))
System.out.println("您不是管理员,没有操作权限");
return;
if (null != next)
next.doHandler(member);
创建执行业务逻辑类
public class BusinessLogicHandler extends Handler
@Override
public void doHandler(Member member)
System.out.println("执行业务逻辑。。");
好,下面写个测试类来测试一下
public class Test
public static void main(String[] args)
Handler.Builder builder = new Handler.Builder();
//这里就是链式编程,谁在前谁在后看的清清楚楚,明明白白
builder.addHandler(new ValidateHandler())
.addHandler(new LoginHandler())
.addHandler(new AuthHandler())
.addHandler(new BusinessLogicHandler());
Member member = new Member();
member.setUsername("");
member.setPassword("");
builder.build().doHandler(member);
执行一下,提示用户名密码不能为空
修改下用户名和密码
执行一下,提示用户名密码不正确
直到把用户名密码权限都设置正确
此时所有验证都通过,开始执行业务逻辑了
源码中的应用
我们来看一个J2EE标准中非常常见的Filter类:
public interface Filter
public default void init(FilterConfig filterConfig) throws ServletException
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException;
public default void destroy()
这个Filter接口的定义非常简单,相当于责任链模型中的handler抽象角色,我们来看Spring中的实现MockFilterChain类:
public class MockFilterChain implements FilterChain
@Nullable
private ServletRequest request;
@Nullable
private ServletResponse response;
private final List<Filter> filters;
@Nullable
private Iterator<Filter> iterator;
public MockFilterChain()
this.filters = Collections.emptyList();
public MockFilterChain(Servlet servlet)
this.filters = initFilterList(servlet);
public MockFilterChain(Servlet servlet, Filter... filters)
Assert.notNull(filters, "filters cannot be null");
Assert.noNullElements(filters, "filters cannot contain null values");
this.filters = initFilterList(servlet, filters);
private static List<Filter> initFilterList(Servlet servlet, Filter... filters)
Filter[] allFilters = ObjectUtils.addObjectToArray(filters, new ServletFilterProxy(servlet));
return Arrays.asList(allFilters);
@Nullable
public ServletRequest getRequest()
return this.request;
@Nullable
public ServletResponse getResponse()
return this.response;
@Override
public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException
Assert.notNull(request, "Request must not be null");
Assert.notNull(response, "Response must not be null");
Assert.state(this.request == null, "This FilterChain has already been called!");
if (this.iterator == null)
this.iterator = this.filters.iterator();
//核心代码执行
if (this.iterator.hasNext())
Filter nextFilter = this.iterator.next();
nextFilter.doFilter(request, response, this);
this.request = request;
this.response = response;
public void reset()
this.request = null;
this.response = null;
this.iterator = null;
...
这里面把链条中所有的 Filter 都放到List<Filter> filters
中, 在 doFilter() 方法中有一段核心的代码this.iterator.hasNext()
,这个就相当于 for 循环的执行 filters 中的 Filter 方法。「虽然写法不同,但也起到了责任链的功能,所以在学习设计模式中,不要拘泥于标准的写法,很多都是变种的,或者写着写着四不像的模式,既像这个设计模式,又像那个设计模式,这个很正常,能起到精简代码,高效运行的都是好代码。」
优缺点
优点:
将请求与处理解耦。
请求处理者(节点对象)只需关注自己感兴趣的请求进行处理,对于不感兴趣的请求,直接转发给下一级节点对象。
具备链式传递处理请求功能,请求发送者无需知晓链路结构,只需等待请求处理结果。
链路结构灵活,可以通过改变链路结构动态地新增或删减责任。
易于扩展新的请求处理类(节点),符合开闭原则。
缺点:
责任链太长或者处理时间过长,会影响整体性能。
如果节点对象存在循环引用时,会造成死循环,导致系统崩溃。
来源:juejin.cn/post/7011490664714240008
以上是关于多重校验神器责任链模式的主要内容,如果未能解决你的问题,请参考以下文章
设计模式责任链模式 ( 简介 | 适用场景 | 优缺点 | 代码示例 )