责任链模式

定义

使多个对象都有机会处理请求,从而避免了请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有对象处理它为止

使用场景

如果一个请求可能会出现多个或未知个数处理器实例,或者请求处理器可动态配置的情况下,这时候便可使用责任链模式。
几乎所有的开源框架中都使用到该模式,如 Spring 中的拦截器、过滤器,通过 ant 表达式风格的 url 参数来判断是否对请求进行拦截,Netty 中 ChannelPipeLine 通过链表的形式添加 ChannelHandler 处理节点。

在SpringBoot中使用

结构图

chain.jpg

责任链的处理核心在"链"(Chain)上面。"链"是由多个处理者 ConcreateX 组成的,我们先来看抽象 “Handler” 接口:

/**
 * @author tianp
 **/
public interface Handler {
    /**
     * 是否支持处理
     *
     * @param uri 匹配uri
     * @return true 成功 false 失败
     */
    boolean support(String uri);

    /**
     * 处理
     *
     * @param requestBody 处理参数封装
     * @return true 成功 false 失败
     */
    boolean handle(RequestBody requestBody);

    /**
     * 获取处理器名字
     *
     * @return 处理器名字
     */
    String getName();

}

RequestBody 对请求的参数进行封装


/**
 * 请求处理封装类
 *
 * @author tianp
 **/
public class RequestBody {
    private String uri;
    //根据业务定义
    private Object body;

    public String getUri() {
        return uri;
    }

    public void setUri(String uri) {
        this.uri = uri;
    }

    public Object getBody() {
        return body;
    }

    public void setBody(Object body) {
        this.body = body;
    }
}

处理器接口 “Hanlder” 定义了三个标准:

  • 是否支持处理。通过参数 uri 判断当前 “Handler” 是否支持处理, 如果当前不能处理就传递给下一个 “Handler” 处理
  • 真正进行处理的方法。传递 RequestBody给 handle 方法,处理成功则传递下一个,不成功则返回false,结束。
  • 获取当前处理器名字。便于打印日志排查问题

/**
 * @author tianp
 **/
public abstract class AbstractHandler implements Handler {
    /**
     * 处理器名称
     */
    private String name;
    /**
     * 拦截uri
     */
    private String[] includePatterns;

    /**
     * 放行uri
     */
    private String[] excludePatterns;

    /**
     * ant 匹配
     */
    private PathMatcher pathMatcher = new AntPathMatcher();
    /**
     * 下一个处理器
     */
    private AbstractHandler next = null;

    public AbstractHandler(String name) {
        this.name = name;
    }

    public boolean support(String uri) {
        if (excludePatterns != null) {
            for (String exclude : excludePatterns) {
                if (pathMatcher.match(exclude, uri)) {
                    return false;
                }
            }
        }
        if (includePatterns != null) {
            for (String include : includePatterns) {
                if (pathMatcher.match(include, uri)) {
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * 给子类实现的真正处理方法
     *
     * @param requestBody 请求参数
     * @return true 成功 false 失败
     */
    public abstract boolean process(RequestBody requestBody);

    public boolean handle(RequestBody requestBody) {
        if (next != null) {
            if (next.support(requestBody.getUri())) {
                System.out.println(next.getName() + "开始处理");
                return next.process(requestBody);
            } else {
                next.handle(requestBody);
            }
        }
        return true;
    }
}
//.... 省略 get/set 方法

抽象处理器 AbstractHandler 实现了 Handler 接口,实现了对应的

  • support() 通过 AntPathMatcher 使用 ant 风格的表达式来匹配当前类是否支持拦截
  • handle() 通过持有一个 next 指针来进行请求传递
  • getName()
  • process() 给子类实现的真正处理的方法
/**
 * @author tianp
 **/
public class LinkedHandlerChain {
    /**
     * 头节点
     */
    private static final AbstractHandler HEAD = new AbstractHandler("head"){

        @Override
        public boolean process(RequestBody requestBody) {
            return true;
        }
    };
    private AbstractHandler TAIL = HEAD;
    public void addLast(AbstractHandler handler){
        TAIL.setNext(handler);
        TAIL = handler;
    }
    public boolean handle(RequestBody requestBody){
       return HEAD.handle(requestBody);
    }
}

最后通过一个链 LinkedhandlerChain 串起来。通过持有链表的指针 HEAD、TAIL 来处理和添加节点。

优点

非常显著的优点就是将请求和处理分开。请求者不用知道是谁处理的,处理者不用知道请求的全貌。两者解耦,提高系统灵活性

缺点

  1. 性能问题。每个请求都是从链头遍历到链尾,特别是在链比较长的时候,性能是一个非常大的问题。
  2. 调试不方便。当链比较长、环节比较多的时候,由于采取了类似递归的方式,调试的时候逻辑可能比较复杂

最佳实践

责任链在实际项目中,曾经在项目中使用它来对一部分功能进行鉴权,因为当时只有一部分接口需要鉴权,如果引入第三方框架如:Spring Security 和 Shiro 不仅增加项目的复杂度,还让项目变 “重”,因此通过引入 责任链模式,我可以很好的解决这个问题

更多参考

代码仓库地址:https://github.com/To-echo/chain-design/tree/master

参考书籍:《设计模式之禅》

posted @ 2019-09-27 10:35  coding400  阅读(636)  评论(0编辑  收藏  举报