首页 > 技术文章 > 责任链模式综述(基础篇)

zsychanpin 2017-08-19 11:26 原文

摘要:

  本篇综述责任链模式的提出动机、原理结构、典型实现和应用场景。并结合详细实例展现了其灵活性、可插拔性和松耦合性。首先。结合我们日常生活中“打扑克”的样例引出了责任链模式产生动机,并揭示了其应用场景。紧接着,我们概述了责任链模式的内涵和结构,即通过建立一条责任链来组织请求的处理者,请求将沿着链进行传递,而请求发送者无须知道请求在何时、何处以及怎样被处理。实现了请求发送者与处理者的解耦。此外,本文给出了责任链模式的典型实现和并结合详细实例介绍其使用方式,以便我们更好体会其长处。


版权声明:

本文原创作者:书呆子Rico
作者博客地址:http://blog.csdn.net/justloveyou_/


友情提示:

  关于责任链模式的介绍共分为两篇,本篇《责任链模式综述(基础篇)》主要介绍了责任链模式的提出动机、原理结构、典型实现和应用场景,解释了责任链模式的本质内涵。本篇的姊妹篇《责任链模式进阶:与AOP思想的融合与应用》主要引入了AOP理念,并在此基础上进一步将责任链结构的实现用方面抽象出来,使得各个详细处理者仅仅需关注自身必须实现的功能性需求。我们知道。不管是Java Web中的过滤器还是Struts2中的Interceptor。它们都是责任链模式与AOP思想互相融合的巧妙实践。为了更进一步理解AOP和CoR,本篇概述了Java Web 的Filter机制,并手动模拟了该机制。

此外。我们还结合Struts2的源码和文档解释了拦截器的工作原理,更进一步剖析了AOP理念和CoR模式在Java中的应用,同一时候也有助于了解Struts2的原理。


一. 引子 —— 责任链模式提出动机与应用场景

  责任链模式在我们生活中有着诸多的应用。比方,在我们玩打扑克的游戏时,某人出牌给他的下家,下家会看看手中的牌,假设要不起上家的牌。则将出牌请求再转发给他的下家,其下家再进行推断,如此重复。一个循环下来。假设其它人都要不起该牌,则最初的出牌者能够打出新的牌。在这个过程中。扑克牌作为一个请求沿着一条链(环)在传递,每一位纸牌的玩家都能够处理该请求。在设计模式中,我们也有一种专门用于处理这种 请求链式传递问题 的模式,即 责任链模式 (Chain of Responsibility Pattern)。

  此外。採购的分级审批问题也是责任链模式的一个应用典范。我们知道。採购审批往往是分级进行的。

也就是说,其经常依据採购金额的不同由不同层次的主管人员来审批。比如。主任能够审批 5 万元下面(不包含 5 万元)的採购单。副董事长能够审批 5 万元至 10 万元(不包含 10 万元)的採购单,董事长能够审批 10 万元至 50 万元(不包含 50 万元)的採购单。50 万元及以上的採购单就须要开董事会讨论决定。

此案比如图所看到的:

             责任链模式图示.gif-12.8kB

  那么,怎样在软件中实现採购单的分级审批呢?这时,假设我们不了解职责链模式。我们给出的解决方式可能是这种:在系统中提供一个採购单处理类 PurchaseRequestHandler 用于统一处理採购单,其框架代码例如以下所看到的:

//採购单处理类 
class PurchaseRequestHandler {

    //递交採购单给主任
    public void handlePurchaseRequest(PurchaseRequest request) {
        if (request.getAmount() < 50000) {
            //由主任审批该採购单
            this.handleByDirector(request);
        }
        else if (request.getAmount() < 100000) {
            //由副董事长审批该採购单
            this.handleByVicePresident(request);
        }
        else if (request.getAmount() < 500000) {
            //由董事长审批该採购单
            this.handleByPresident(request);
        }
        else {
            //由董事会审批该採购单
            this.handleByCongress(request);
        }
    }

    //主任审批採购单
    public void handleByDirector(PurchaseRequest request) {
        ...
    }

    //副董事长审批採购单
    public void handleByVicePresident(PurchaseRequest request) {
        ...
    }

    //董事长审批採购单
    public void handleByPresident(PurchaseRequest request) {
        ...
    }

    //董事会审批採购单
    public void handleByCongress(PurchaseRequest request) {
        ...
    }
}

  上述方案似乎非常完美,但细致分析。发现其存在例如以下几个问题:

  • PurchaseRequestHandler 类较为庞大,各个级别的审批方法都集中在一个类中。违反了“单一职责原则”。測试和维护难度大。

  • 当须要添加一个新的审批级别或调整不论什么一级的审批金额和审批细节(比如将董事长的审批额度改为 60 万元)时。都必须改动源码并进行严格測试;此外,假设须要移除某一级别(比如金额为 10 万元及以上的採购单直接由董事长审批,不再设副董事长一职)时也必须对源码进行改动,违反了“开闭原则”;

  • 审批流程的设置 缺乏灵活性。如今的审批流程是“主任–>副董事长–>董事长–>董事会”,假设须要改为“主任–>董事长–>董事会”,在此方案中仅仅能通过改动源码来实现,client无法定制审批流程。


  那么。我们怎样针对上述问题对系统进行改进呢?我们能够通过使用 责任链模式 提出一种 灵活的 可插拔式的 解决方式,它能够确保最大程度地解决这些问题。


二. CoR 模式概述

  非常多情况下,在一个软件系统中能够处理某个请求的对象不止一个。比如上面提到的採购审批子系统。主任、副董事长、董事长和董事会都能够处理採购单,他们能够构成一条处理採购单的链式结构。採购单(能够看作是要处理的信息)沿着这条链进行传递。这条链就称为责任链。

责任链能够是一条直线、一个环或者一个树形结构。最常见的职责链是直线型,即沿着一条单向的链来传递请求,例如以下图所看到的。链上的每个对象都是请求处理者。责任链模式能够将请求的处理者组织成一条链,并让请求沿着链传递,由链上的处理者对请求进行对应的处理。

在此过程中。client实际上无须关心请求的处理细节以及请求的传递,仅仅需将请求发送到链上就可以,从而实现请求发送者和请求处理者解耦。
  
          COR2.png-10.6kB

  对责任链的理解,关键在于对链的理解,即包含例如以下两点:

  • 链是一系列节点的集合,在责任链中,节点实质上是指请求的处理者。

  • 链的各节点可灵活拆分再重组,在责任链中,实质上就是请求发送者与请求处理者的解耦。


三. CoR 模式定义与结构

  顾名思义,责任链模式(Chain of Responsibility Pattern) 指的是用一系列类(classes)去处理一个请求request。这些类之间是一个松散的耦合,它们之间的唯一联系就是在它们之间传递的 request。也就是说,假设来了一个请求,那么A类先处理。若A类处理不了。就传递到B类进行处理。假设B类也处理不了,就传递给C类进行处理,这些请求处理类像一个链条(chain)一样,请求在这条链上不断的传递下去。直至其被处理。


1、责任链模式的定义

定义: 使多个对象都有机会处理请求,从而避免请求的发送者与请求处理者耦合在一起。将这些对象连接成一条链,而且沿着这条链传递请求。直到有对象处理它为止。

类型: 对象行为型模式

实质: 责任链上的处理者负责处理请求。客户仅仅须要将请求发送到职责链上就可以。无须关心请求的处理细节和请求的传递。从而实现请求发送者与请求处理者的解耦。


2、责任链模式的结构

             这里写图片描写叙述

  我们能够从责任链模式的结构图中看到,详细的请求处理者能够有多个,而且所有的请求处理者均具有同样的接口(继承于同一抽象类)。 责任链模式主要包含例如以下两个角色:

  • Handler(抽象处理者):处理请求的接口。一般设计为具有抽象请求处理方法的抽象类。以便于不同的详细处理者进行继承。从而实现详细的请求处理方法。此外,因为每个请求处理者的下家还是一个处理者,因此抽象处理者本身还包含了一个本身的引用( successor)作为其对下家的引用,以便将处理者链成一条链;

  • ConcreteHandler(详细处理者):抽象处理者的子类。能够处理用户请求。其实现了抽象处理者中定义的请求处理方法。

    在详细处理请求时须要进行推断,若其具有对应的处理权限。那么就处理它;否则。其将请求 转发 给后继者。以便让后面的处理者进行处理。

      在责任链模式里,由每个请求处理者对象对其下家的引用而连接起来形成一条请求处理链。

    请求将在这条链上一直传递。直到链上的某一个请求处理者能够处理此请求。其实。发出这个请求的client并不知道链上的哪一个请求处理者将处理这个请求。这使得系统能够在不影响client的情况下动态地又一次组织链和分配责任。


三. CoR 模式的典型实现

   实现责任链模式的关键代码是: 在抽象类 Handler 里面聚合它自己(持有自身类型的引用)。并在 handleRequest 方法里推断其能否够处理请求。

若当前处理者无法处理,则设置其后继者并向下传递,直至请求被处理。


1、对请求处理者的抽象

  责任链模式的核心在于对 请求处理者的抽象。在实现过程中。抽象处理者通常会被设定为 抽象类,其典型实现代码例如以下所看到的:

public abstract class Handler {

    // protected :维持对下家的引用
    protected Handler successor;

    public void setSuccessor(Handler successor) {
        this.successor=successor;
    }

    public abstract void handleRequest(String request);
}

  上述代码中,抽象处理者类定义了对下家的引用 (其一般用 protected 进行修饰),以便将请求转发给下家,从而形成一条请求处理链。同一时候。在抽象处理者类中还声明了抽象的请求处理方法,以便由子类进行详细实现。

  很多其它关于关键字protected的使用。请移步我的博文《Java 訪问权限控制:你真的了解 protected 关键字吗?》


2、对请求处理者的抽象

  详细处理者是抽象处理者的子类,详细处理者类的典型代码例如以下:

public class ConcreteHandler extends Handler {
    public void handleRequest(String request) {
        if (请求满足条件) {
            //处理请求
        }else {
            this.successor.handleRequest(request);  //转发请求
        }
    }
}

  
  在详细处理类中,通过对请求进行推断以便做出对应的处理。因此,其一般具有两大作用:

  • 处理请求。不同的详细处理者以不同的形式实现抽象请求处理方法 handleRequest();

  • 转发请求。若该请求超出了当前处理者类的权限。能够将该请求转发给下家;


3、责任链的创建

  须要注意的是,责任链模式并不创建职责链,职责链的创建工作必须由系统的其它部分来完毕。一般由使用该责任链的client创建。

职责链模式减少了请求的发送者和请求处理者之间的耦合。从而使得多个请求处理者都有机会处理这个请求。


四. COR模式应用实例

  为了让审批流程更加灵活并实现採购单的链式传递和处理,我们如今使用职责链模式来实现採购单的分级审批,其基本结构如图所看到的:

           这里写图片描写叙述

  在上图中,抽象类 Approver 充当抽象处理者,而 Director、VicePresident、President 和 Congress 则充当详细处理者,PurchaseRequest 充当请求消息类。完整代码例如以下所看到的:


(1). 请求消息类

//请求消息类(採购单)
public class PurchaseRequest {
    private double amount;  //採购金额
    private int number;  //採购单编号
    private String purpose;  //採购目的

    // 构造器
    public PurchaseRequest(double amount, int number, String purpose) {
        this.amount = amount;
        this.number = number;
        this.purpose = purpose;
    }

    // setter & getter 
    public void setAmount(double amount) {
        this.amount = amount;
    }

    public double getAmount() {
        return this.amount;
    }

    public void setNumber(int number) {
        this.number = number;
    }

    public int getNumber() {
        return this.number;
    }

    public void setPurpose(String purpose) {
        this.purpose = purpose;
    }

    public String getPurpose() {
        return this.purpose;
    }
}

(2). 抽象处理者类 (抽象类)

// 抽象处理者(审批者类)
public abstract class Approver {

    protected Approver successor;  //定义后继处理对象

    //设置后继者
    public void setSuccessor(Approver successor) { 
        this.successor = successor;
    }

    //抽象请求处理方法
    public abstract void processRequest(PurchaseRequest request);
}

(3). 详细处理者类 (抽象处理者的子类)

//主任类:详细处理者
public class Director extends Approver {

    //详细请求处理方法
    public void processRequest(PurchaseRequest request) {
        if (request.getAmount() < 50000) {
            System.out.println("主任" +"审批採购单:" + request.getNumber() + ",金额:" + request.getAmount() + "元。採购目的:" + request.getPurpose() + "。");  // 处理请求
        }else {
            this.successor.processRequest(request);    // 转发请求
        }   
    }
}


//副董事长类:详细处理者
public class VicePresident extends Approver {

    //详细请求处理方法
    public void processRequest(PurchaseRequest request) {
        if (request.getAmount() < 100000) {
            System.out.println("副董事长" + "审批採购单:" + request.getNumber() + ",金额:" + request.getAmount() + "元,採购目的:" + request.getPurpose() + "。

"); // 处理请求 }else { this.successor.processRequest(request); // 转发请求 } } } //董事长类:详细处理者 public class President extends Approver { //详细请求处理方法 public void processRequest(PurchaseRequest request) { if (request.getAmount() < 500000) { System.out.println("董事长" + "审批採购单:" + request.getNumber() + "。金额:" + request.getAmount() + "元。採购目的:" + request.getPurpose() + "。"); // 处理请求 }else { this.successor.processRequest(request); // 转发请求 } } } //董事会类:详细处理者 public class Congress extends Approver { //详细请求处理方法 public void processRequest(PurchaseRequest request) { System.out.println("召开董事会审批採购单:" + request.getNumber() + ",金额:" + request.getAmount() + "元。採购目的:" + request.getPurpose() + "。"); //处理请求 } }


(4). client測试代码

public class Client {
    public static void main(String[] args) {
        Approver director,vicePresident,president,congress;
        director = new Director();
        vicePresident = new VicePresident();
        president = new President();
        congress = new Congress();

        //创建责任链
        director.setSuccessor(vicePresident);
        vicePresident.setSuccessor(president);
        president.setSuccessor(congress);

        //创建採购单,并从主任開始处理
        PurchaseRequest pr1 = new PurchaseRequest(45000,10001,"购买倚天剑");
        director.processRequest(pr1);

        PurchaseRequest pr2 = new PurchaseRequest(60000,10002,"购买《葵花宝典》");
        director.processRequest(pr2);

        PurchaseRequest pr3 = new PurchaseRequest(160000,10003,"购买《金刚经》");
        director.processRequest(pr3);

        PurchaseRequest pr4 = new PurchaseRequest(800000,10004,"购买桃花岛");
        director.processRequest(pr4);
    }
}/* Output(): 
        主任审批採购单:10001,金额:45000.0元。採购目的:购买倚天剑。
        副董事长审批採购单:10002。金额:60000.0元,採购目的:购买《葵花宝典》。

董事长审批採购单:10003,金额:160000.0元,採购目的:购买《金刚经》。

召开董事会审批採购单:10004,金额:800000.0元,採购目的:购买桃花岛。

*///:~


五. CoR 模式的灵活性

1、CoR 模式的可扩展性

  我们在上一节中已经给出了使用责任链模式解决审批问题的完整解决方式。假设须要在系统添加一个新的详细处理者,比方添加一个经理(Manager)角色能够审批 5 万元至 8 万元(不包含 8 万元)的採购单,则仅仅须要编写一个抽象处理者类 Approver 的新的子类 Manager,并实如今 Approver 类中定义的抽象处理方法。假设採购金额大于等于 8 万元。则将请求转发给下家。代码例如以下所看到的:

//经理类:详细处理者
class Manager extends Approver {
    public Manager(String name) {
        super(name);
    }

    //详细请求处理方法
    public void processRequest(PurchaseRequest request) {
        if (request.getAmount() < 80000) {
            System.out.println("经理" + "审批採购单:" + request.getNumber() + ",金额:" + request.getAmount() + "元,採购目的:" + request.getPurpose() + "。");  //处理请求
        }else {
            this.successor.processRequest(request);  //转发请求
        }   
    }
}

  因为链的创建过程由client负责。因此添加新的详细处理者类不会对原有程序有不论什么影响。无须改动已有类的源码,符合“开闭原则”。此外,在client代码中,若须要将新的详细请求处理者应用在系统中,则仅仅需创建该详细处理者对象并将其加入责任链中就可以。即仅仅需添加例如以下代码:

Approver manger = new Manager();   // 在client创建新的请求处理者

//在client创建责任链。仅仅需将经理角色在适当位置链入到责任链中就可以
director.setSuccessor(manger);    //将经理作为主任的下家
manger.setSuccessor(vicePresident);  //将副董事长作为经理的下家
vicePresident.setSuccessor(president);
president.setSuccessor(congress);

2、CoR 模式的长处

  • 减少耦合度,使请求的发送者和接收者解耦,便于灵活的、可插拔的定义请求处理过程;

  • 简化、封装了请求的处理过程,而且这个过程对client而言是透明的,以便于动态地又一次组织链以及分配责任,增强请求处理的灵活性;

     在上面的演示样例中,这两个长处主要体现为例如以下两点:第一。我们能够随时改变内部的请求处理规则。每个请求处理者都能够去动态地指定他的继任者。

    也就是说。主任全然能够跳过副董事长直接找到董事长进行审批。第二,我们能够从职责链不论什么一个节点開始。即假设主任不在。能够直接去找副董事长,责任链还会继续,不会有不论什么影响。

     其实,假设我们不使用责任链,我们须要和公司中的每个层级都发生耦合关系,反映在代码上就是我们须要在一个类中写上非常多丑陋的 if….else 语句。

    假设我们使用了责任链。相当于我们面对的是一个黑箱子,我们仅仅须要认识当中的一个部门。然后让黑箱内部去负责处理和传递就好了。


六. CoR 模式的类型

  严格地来说,职责链模式可分为纯的职责链模式和不纯的职责链模式两种。

1、纯的职责链模式

  一个纯的职责链模式要求一个详细处理者对象仅仅能在两个行为中选择一个:要么承担所有责任,要么将责任推给下家,而不同意出现某一个详细处理者对象在承担了一部分或所有责任后又将责任向下传递的情况。而且在纯的职责链模式中,要求一个请求必须被某一个处理者对象所接收,不能出现某个请求未被不论什么一个处理者对象处理的情况。

我们前面提到的採购单审批演示样例应用的是纯的职责链模式。


2、不纯的职责链模式

  在一个不纯的职责链模式中,同意某个请求被一个详细处理者部分处理后再向下传递,或者一个详细处理者处理完某请求后其后继处理者继续处理该请求,而且一个请求能够终于不被不论什么处理者对象所接收。


  实际上,纯的责任链模式的实际样例中的应用非常难找到。我们一般看到的样例均是不纯的责任链模式的实现。比方 Struts 中的拦截器(Interceptor)。 Java Web 中的 Filter 等都是责任链模式在Java中的详细应用实例。

  关于责任链模式模式在Java中的应用(AOP 理念。 Java Web 中的Filter 和 Struts2中的 Interceptor)请见本篇的姊妹篇《 责任链模式进阶:与AOP思想的融合与应用》


七. CoR 模式的总结

  责任链模式通过建立一条链来组织请求的处理者,请求将沿着链进行传递。而请求发送者无须知道请求在何时、何处以及怎样被处理,实现了请求发送者与处理者的解耦。在实际软件开发中,假设遇到有多个对象能够处理同一请求时能够考虑使用职责链模式,最常见的样例包含在 Java Web 应用开发中创建一个过滤器(Filter)链来对请求数据进行过滤(中文字符乱码的处理)、在工作流系统中实现公文的分级审批、在Struts应用中加入不同的拦截器(经常使用的有类型转化、异常处理,数据校验…)以增强Struts2的功能等。


八. 很多其它

  很多其它关于 protected 关键字 的介绍, 请移步我的博文《Java 訪问权限控制:你真的了解 protected 关键字吗?》

  很多其它关于 责任链模式模式在Java中的应用(特别是在Java Web 和 Struts中的应用), 请移步我的博文 《 责任链模式进阶:与AOP思想的融合与应用》


  须要指出的是。本文关于责任链模式理论部分的介绍借鉴了极客学院的《设计模式之行为型模式》一文中的关于责任链模式的案例和应用实例。


引用

《JAVA与模式》之责任链模式
Java设计模式之责任链模式、职责链模式
职责链模式(Chain of Responsibility)的Java实现
Java模式之责任链模式
责任链模式
责任链模式
职责链模式

推荐阅读