设计模式(十二):责任链模式(ChainofResponsibility)

star2017 1年前 ⋅ 369 阅读

生活或工作中经常会遇到一种情境,即要完成或处理某件任务时,需要经过层层传递到某一层级才能得到处理。

例如,加薪审批,直接的小组长可能并没有权限,需要向上报批,可能需要经过好几层到副总级别才得到处理。例如,击鼓传花游戏,花在多个人之间传递把大家连接起来,鼓声落,谁持有花谁就喝酒。

这种情境在软件设计中也是存在的,多个对象之间相互引用而形成一条链,最终链中的某一个对象处理了任务,而客户端并不需要知道具体是谁处理的,对象也不需要知道任务具体来自谁, 这就是责任链的最简单模型。

模式定义

责任链模式(Chain of Responsibility)是一种对象行为模式。在责任链模式中,多个对象由每一个对象对其下家的引用而连接起来形成一条链。

职责链模式:Chain of Responsibility ,处理者负责处理请求,客户端只需要将请求发送到到职责链即可,无须关心请求的处理细节和请求的传递,请求链中的使多个对象都有机会处理请求,从而将请求的发送者和处理者解耦。

职责链模式,将所有可能处理请求的对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。当客户端提交一个请求时,请求是沿链传递直到一个具体处理对象负责处理它。发送者和处理者都没有对方的明确信息,且链中的对象自己也并不知道链的结构。

职责链模式,可简化对象的相互连接,他们仅需要保持一个指向后继者的引用,而不需保持它所有的候选接受者的引用(很像单向链表结构)。这大大降低了耦合度,可以随时地增加或修改处理一个请求的结构,增强了给对象指派职责的灵活性。

最重要的两点:一个是判断是否可以处理请求,另一个是设置下一个处理者。

纯的责任链

一个纯的责任链模式要求一个具体的处理者要么承担责任处理请求,要么把责任推给下家。不允许出现在处理了一部分责任后又把责任向下传的情况。

在一个纯的责任链模式里面,一个请求必须被某一个处理对象所接收。

不纯的责任链

一个不纯的责任链模式,允许某一个具体处理对象在处理了请求的一部分责任后又将剩余的责任传给下家的情况,且一个请求可以最终不被任何接收端对象所接收。

纯的责任链模式很难找到实际的例子,一般看到的均是不纯的责任链模式的实现。

模式分析

模式结构

职责链模式与数据结构的单向链表很相似,可以通过链表来实现职责链模式的数据结构。

  • 抽象处理者(Handler):定义一个处理请求的接口,包含一个用于设置和返回下家的引用。通常由一个抽象类或接口实现。
  • 具体处理者(ConcreteHandler):实现抽象处理者的处理方法,在接到请求后,处理自己的任务,不能处理则将请求传给下家。

责任链模式并不创建出责任链,必须由系统的其他部分创建出来。

责任链可以是一条线,一个树,也可以是一个环。链的拓扑结构可以是单连通的或多边通的,责任链并不指定责任链的拓扑结构。但责任链模式要求在同一个时间里,命令只能被传给一个下家(或被处理掉),而不可以传给多个下家。

优缺点

优点

  • 降低请求发送者和处理者之间的耦合度。
  • 简化了对象之间的连接关系。每个对象不需要知道链的结构,只需保持一个指向其后继者的引用。
  • 增强了给对象批派职责的灵活性。可以动态地改变链内的成员或者调动它们的顺序,可动态地新增或删除处理者。
  • 增强了系统的可扩展性。可以根据需要增加新的请求处理类,满足开闭原则。
  • 责任分担。每个对象只需处理自己的任务,不该处理的传递给下一个对象,明确各类的责任范围,符合类的单一职责原则。

缺点

  • 不能保证请求一定会被处理,可能一直传到链尾都得不到处理。
  • 如果职责链比较长,请求处理可能涉及多个处理对象,将影响系统性能。
  • 不方便代码调试,不容易观察运行时的特征,不利于排错。
  • 职责链中的处理者顺序由客户端指定,增加了客户端的复杂性,若设置错误,则可能造成循环引用而导致系统出错。

应用场景

  • 有多个对象可以处理一个请求,具体哪个对象处理该请求由运行时刻自动确定。
  • 在不明确指定请求处理者的情况下,向多个处理者中的一个提交请求。
  • 需要动态指定一组对象处理请求,或添加新的处理者。

模式应用

经典应用

Servlet 过滤器 Filter 可以拦截客户端的请求,在后端服务处理之前做一些预处理。

  1. Servlet 定义了过滤器接口 Filter 和 过滤器链接口 FilterChain,源码如下:

    Filter.class

    public interface Filter {
        public void init(FilterConfig filterConfig) throws ServletException;
        public void doFilter(ServletRequest request, ServletResponse response,
                FilterChain chain) throws IOException, ServletException;
        public void destroy();
    }
    

    FilterChain.class

    public interface FilterChain {
        public void doFilter(ServletRequest request, ServletResponse response)
                throws IOException, ServletException;
    }
    
  2. 自定义过滤器

    public class MyFilter implements Filter {
        @Override
        public void init(FilterConfig filterConfig) throws ServletException {
        }
    
        @Override
        public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
    
            System.out.println("执行 doFilter() 方法之前...");
            chain.doFilter(request, response);
            System.out.println("执行 doFilter() 方法之后...");
        }
    
        @Override
        public void destroy() {
        }
    }
    
  3. 详细可参考

    责任链模式及典型应用

    • Netty 中的 Pipeline 和 ChannelHandler 通过责任链设计模式来组织代码逻辑
    • Spring Security 使用责任链模式,可以动态地添加或删除责任(处理 request 请求)
    • Spring AOP 通过责任链模式来管理 Advisor
    • Dubbo Filter 过滤器链也是用了责任链模式(链表),可以对方法调用做一些过滤处理,譬如超时(TimeoutFilter),异常(ExceptionFilter),Token(TokenFilter)等
    • Mybatis 中的 Plugin 机制使用了责任链模式,配置各种官方或者自定义的 Plugin,与 Filter 类似,可以在执行 Sql 语句的时候做一些操作

示例代码

需求:模拟一个请假审批链条

  1. 处理者抽象

    /**
     * 处理者抽象
     */
    public abstract class AbstractHandler {
    
        private AbstractHandler nextHandler;
    
        public abstract void handleRequest(Object obj);
    
        public AbstractHandler getNextHandler() {
            return nextHandler;
        }
    
        public AbstractHandler setNextHandler(AbstractHandler nextHandler) {
            this.nextHandler = nextHandler;
            return this;
        }
    }
    
  2. 请假表单

    public class TakeLeave {
        private String name;
        private String role;
        private Integer days;
        private Map<String, String> statusMap = new HashMap<>();
        //------set/get 方法--------
    }
    
  3. 具体处理者

    组长审批

    /**
     * 具体处理者:组长
     */
    public class TeamLeaderHandler extends AbstractHandler {
    
        @Override
        public void handleRequest(Object obj) {
            TakeLeave takeLeave = (TakeLeave) obj;
            Map<String, String> statusMap = takeLeave.getStatusMap();
            statusMap.put("TeamLeader", "Agree");
    
            if (takeLeave.getDays() > 3) {
                getNextHandler().handleRequest(takeLeave);
            }
    
        }
    }
    

    部门经理审批

    /**
     * 具体处理者:部门经理
     */
    public class DeptManagerHandler extends AbstractHandler {
    
        @Override
        public void handleRequest(Object obj) {
            TakeLeave takeLeave = (TakeLeave) obj;
            Map<String, String> statusMap = takeLeave.getStatusMap();
            statusMap.put("DeptManager", "Agree");
    
            if (takeLeave.getDays() > 7) {
                getNextHandler().handleRequest(takeLeave);
            }
        }
    }
    

    总经理审批

    /**
     * 具体处理者:总经理
     */
    public class GeneralManagerHandler extends AbstractHandler {
    
        @Override
        public void handleRequest(Object obj) {
            TakeLeave takeLeave = (TakeLeave) obj;
            Map<String, String> statusMap = takeLeave.getStatusMap();
            statusMap.put("GeneralManager", "Agree");
        }
    }
    
  4. 客户端提交请假审批请求

    public class MainTest {
    
        public static void main(String[] args) {
            AbstractHandler teamLeaderHandler = new TeamLeaderHandler();
            AbstractHandler deptManagerHandler = new DeptManagerHandler();
            AbstractHandler generalManagerHandler = new GeneralManagerHandler();
    
            //组装职责链(在客户端组状职责链,增加了客户端的复杂性)
            teamLeaderHandler.setNextHandler(deptManagerHandler);
            deptManagerHandler.setNextHandler(generalManagerHandler);
            //请假表单
            TakeLeave takeLeave = new TakeLeave("Kitty", 8);
            //提交请求
            teamLeaderHandler.handleRequest(takeLeave);
    
            System.out.println(JSON.toJSONString(takeLeave));
        }
    }
    
  5. 输出结果

    {
        "days": 8,
        "name": "Kitty",
        "statusMap": {
            "GeneralManager": "Agree",
            "TeamLeader": "Agree",
            "DeptManager": "Agree"
        }
    }
    

其它参考

  1. Apache Commons chain
  2. Commons-chain:责任链框架
  3. 责任链模式(职责链模式)详解
  4. 责任链模式及典型应用
更多内容请访问:IT源点

全部评论: 0

    我有话说: