如何在软件中贯彻买卖单的独家审批,并让请求沿着链传递

【学习难度:★★★☆☆,使用功能:★★☆☆☆】

【学习难度:★★★☆☆,使用频率:★★☆☆☆】
向来出处:http://woquanke.com/books/gof/
梳理和学习:https://github.com/BruceOuyang/boy-design-pattern
简书日期: 2018/03/15
简书首页:https://www.jianshu.com/p/0fb891a7c5ed

实战案例: ** 购销单的分别审批**

Sunny软件公司程节了一个买进审批子系统,其购得审批是独家举办的,依照不一致的购置金额由不同层次的掌管举办审批。老总审批5万一眨眼,副董事长审批5万到10万。董事长审批10万-50万的购入单。其流程如图所示:

图片 1

选购流程

在贯彻各自审批的时候,小明开发元指出了多少个始发消除方案,使用PurchaseRequestHandler统一处理购销单,框架代码如下:

Class PurchaseRequestHandler{
  public void sendRequestToDirectror(PurchaseRequest request){
    if(request.getAmount()<50000)
      //主管审批
      this.handlerByDirector(request);
    else if(request.getAmount()<100000)
      //副董事长审批采购
      this.handlerByVicePresident(request);
......//省略一大堆
  }
}

因而上述代码,大家分析一下,会有那多少个难点:

  1. 如果审批流程更长,会招致该类代码愈多,且各种级其他审批都在二个类中,违反了单纯性任务规范
    ,测试维护难度大。

  2. 修改审批细节都不只怕不对源代码举行修改并测试,违反了开闭原则。

  3. 审批流程不够灵活性,只好通过改源代码完成流程的改动,不能动态调整。
    上述难题想要消除,大家得以行使任务链形式,下边就让大家开辟新世界的大门吧。

 

恳请的链式处理——任务链格局(一)

“一对二”,“过”,“过”……那声音熟稔吗?你会想到如何?对!纸牌。在类似“斗地主”这样的纸牌游戏中,某人出牌给他的下家,下家看看手中的牌,如若要不起上家的牌则将出牌请求再转载给他的下家,其下家再开展判定。多个循环往复下来,借使其余人都要不起该牌,则最初的出牌者可以打出新的牌。在那个过程中,牌作为二个呼吁沿着一条链在传递,每一人纸牌的玩家都可以拍卖该请求。在设计情势中,大家也有一种更加用于拍卖这种请求链式传递的格局,它就是本章将要介绍的天职链情势。

16.1 买卖单的个别审批

Sunny软件公司承接了某商户SCM(Supply Chain
Management,供应链管理)系统的开支职务,其中蕴藏三个置办审批子系统。该店铺的购买审批是个别举办的,即基于购买销售金额的不等由不相同层次的老板人士来审批,主管可以审批5万元以下(不蕴含5万元)的买入单,副董事长可以审批5万元至10万元(不包涵10万元)的选购单,董事长可以审批10万元至50万元(不包蕴50万元)的进货单,50万元及以上的购买单就须要开董事会研究决定。如图16-1所示:

图片 2

图16-1 采购单分级审批示意图

如何在软件中贯彻买卖单的分别审批?Sunny软件商店开发人士提议了2个开首化解方案,在系统中提供贰个买卖单处理类PurchaseRequestHandler用于统一处理购销单,其框架代码如下所示:

//采购单处理类  
class PurchaseRequestHandler {  
    //递交采购单给主任  
    public void sendRequestToDirector(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) {  
        //代码省略  
    }  
}

难题一般很不难,但细心分析,发现上述方案存在如下多少个难题:

(1)PurchaseRequestHandler类较为庞大,各类级别的审批办法都汇聚在一个类中,违反了“单一任务规范”,测试和保证难度大。

(2)假诺急需追加二个新的审批级别或调整其他一流的审批金额和审批细节(例如将董事长的审批额度改为60万元)时都必须修改源代码并举行严刻测试,别的,假使急需移除某拔尖别(例如金额为10万元及以上的购买销售单直接由董事长审批,不再设副董事长一职)时也亟须对源代码举办改动,违反了“开闭原则”。

(3)审批流程的设置不够灵活性,以往的审批流程是“COO–>副董事长–>董事长–>董事会”,就算急需改为“COO–>董事长–>董事会”,在此方案中只可以经过修改源代码来贯彻,客户端无法定制审批流程。

怎么着针对上述难题对系统举行改正?Sunny公司开发人士殷切须求一种新的设计方案,幸好有任务链方式,通过行使任务链方式大家得以最大程度地化解那么些标题,上边让我们专业进入职分链形式的上学。

职责链概述

任务链情势能够将请求的处理者社团成一条链,并让请求沿着链传递,由链上的拍卖着对请求举行处理,客户端无需关注请求的拍卖细节以及呼吁的传递,只需将请求发送到链上即可,达成请求发送者和请求处理者的解耦。

定义:职责链情势(Chain of Responsibility
Pattern):防止请求发送者与接受者耦合在联合,让几个对象都有或者收到请求,将那几个对象连接成一条链,并且沿着那条链传递请求,知道有目的处理它截至。职分链形式是一种对象行为形式。

职分链方式结构的为主在于引入了抽象处理者:如图:

图片 3

职务链方式结构图

义务链形式结构图中涵盖如下多少个剧中人物:

  • Handler
    定义了拍卖请求的接口,一般设计为抽象类,由具体不一样的现实处理着拍卖请求的点子分歧,由此定义抽象请求处理形式。因为各类处理者的下家还是二个处理者,由此在抽象处理者定义了1个虚无处理者类型的对象,作为下家引用。通过该引用,处理着可以连成一条链。
  • ConcreteHandler(具体处理者):他是空虚处理者的子类,能够拍卖用户请求,在具体处理者类中完结了抽象处理者定义的虚幻请求处理办法,在处理请求以前举行判断,看是否有照应的处理权限,如果可以拍卖请求就处理它,否则转载给后者;在具体处理者中得以访问链中下3个对象,两遍请求的转速。
    在职分链中:很多目标由每一种目的对其下家的引用而链接起来形成一条链。请求在那一个链上传递,知道链上某些对象说了算拍卖刺请求。发出那一个请求的客户端并不知道链上的哪四个对象末了处理那几个请求,那使得系统可以在不影响客户端的动静下动态地再一次社团链和分配义务。

“一对二”,“过”,“过”……这声音熟知吗?你会想到什么?对!纸牌。在类似“斗地主”那样的纸牌游戏中,某人出牌给他的下家,下家看看手中的牌,就算要不起上家的牌则将出牌请求再转载给他的下家,其下家再展开判断。贰个循环往复下来,假如其余人都要不起该牌,则最初的出牌者可以打出新的牌。在那些过程中,牌作为1个呼吁沿着一条链在传递,每壹人纸牌的玩家都可以拍卖该请求。在设计形式中,大家也有一种专门用来拍卖这种请求链式传递的方式,它就是本章将要介绍的职务链格局。

伸手的链式处理——任务链形式(二)

16.2 义务链形式概述

过多气象下,在多个软件系统中得以拍卖某些请求的靶子不止1个,例如SCM系统中的买卖单审批,COO、副董事长、董事长和董事会都足以处理购买销售单,他们得以组成一条处理购买销售单的链式结构,购销单沿着那条链进行传递,那条链就叫做义务链。职务链可以是一条直线、三个环或许3个树形结构,最广泛的职务链是直线型,即沿着一条单向的链来传递请求。链上的每贰个对象都以呼吁处理者,职责链格局可以将呼吁的处理者协会成一条链,并让请求沿着链传递,由链上的处理者对请求举办对应的拍卖,客户端无须关切请求的处理细节以及呼吁的传递,只需将请求发送到链上即可,完成请求发送者和哀求处理者解耦。

职分链情势定义如下: 义务链方式(Chain of Responsibility
Pattern):防止请求发送者与接收者耦合在联名,让七个对象都有恐怕收到请求,将那个对象连接成一条链,并且沿着那条链传递请求,直到有目的处理它为止。义务链方式是一种对象行为型格局。

职务链形式结构的基本在于引入了1个抽象处理者。义务链格局结构如图16-2所示:

图片 4

图16-2 职分链方式结构图

在职务链形式结构图中隐含如下多少个剧中人物:

  • Handler(抽象处理者):它定义了多少个处理请求的接口,一般设计为抽象类,由于不相同的具体处理者处理请求的方法各异,由此在中间定义了抽象请求处理办法。因为每2个处理者的下家如故三个处理者,因此在架空处理者中定义了三个虚幻处理者类型的目的(如结构图中的successor),作为其对下家的引用。通过该引用,处理者可以连成一条链。

  • ConcreteHandler(具体处理者):它是虚幻处理者的子类,能够拍卖用户请求,在具体处理者类中达成了画饼充饥处理者中定义的肤浅请求处理办法,在处理请求从前必要进行判断,看是还是不是有照应的处理权限,倘诺得以拍卖请求就处理它,否则将呼吁转载给后继者;在具体处理者中可以访问链中下一个对象,以便请求的转载。

在职务链格局里,很多目的由每壹个目标对其下家的引用而连接起来形成一条链。请求在那个链上传递,直到链上的某一个对象说了算拍卖此呼吁。发出那个请求的客户端并不知道链上的哪壹个目的最后处理这几个请求,那使得系统能够在不影响客户端的状态下动态地再一次社团链和分红义务。

职分链形式的基本在于抽象处理者类的宏图,抽象处理者的典型代码如下所示:

abstract class Handler {

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

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

    public abstract void handleRequest(String request);  
}

上述代码中,抽象处理者类定义了对下家的引用对象,以便将请求转发给下家,该对象的拜访符可设为protected,在其子类中可以使用。在空虚处理者类中扬言了抽象的伸手处理格局,具体落到实处交由子类落成。

现实处理者是空虚处理者的子类,它富有两大成效:第三是处理请求,不同的现实处理者以不一致的款型完结抽象请求处理办法handleRequest();第①是转账呼吁,假使该请求超出了脚下处理者类的权杖,可以将该请求转载给下家。具体处理者类的头名代码如下:

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

在切切实实处理类中经过对请求进行判定可以做出相应的拍卖。

亟待小心的是,职务链形式并不创立职分链,职分链的创导工作务必由系统的其他部分来成功,一般是在行使该义务链的客户端中成立义务链。职务链情势下降了请求的发送端和接收端之间的耦合,使五个对象都有时机处理这一个请求。

思考

什么在客户端创制一条任务链?

纯与不纯的天职链情势

  1. 纯的任务链方式:
    壹个纯的任务链情势须要3个实际处理者对象只可以在多少个表现中拔取贰个:要么承担任何责任,要么将权利推给下家,不允许现身某三个具体处理者对象在承受了一有的或任何权责后
    又将任务向下传递的事态。而且在纯的任务链格局中,须求2个呼吁必须被某几个处理者对
    象所接到,不可以冒出有些请求未被此外2个处理者对象处理的情景。在前面的购置单审批实
    例中应用的是纯的职分链形式。
  2. 不纯的职务链格局:
    在三个不纯的职责链形式中允许某些请求被贰个实际处理者部分处理后再向下传递,或许二个有血有肉处理者处理完某请求后其后继处理者可以连续处理该请求,而且三个呼吁可以最后不
    被其余处理者对象所接到。Java AWT
    1.0中的事件处理模型应用的是不纯的天职链形式,其基
    本原理如下:由于窗口组件(如按钮、文本框等)一般都置身容器组件中,由此当事件暴发在某一个零部件上时,先通过组件对象的handle伊夫nt()方法将事件传递给相应的事件处理方法,
    该事件处理方法将处理此事件,然后决定是还是不是将该事件向上一级容器组件传播;上级容器组
    件在收到事件随后可以两次三番处理此事件并控制是不是持续向下边容器组件传播,如此反复,直
    到事件到达顶层容器组件截至;如果一贯传到最顶层容器仍尚未处理方法,则该事件不予处
    理。每拔尖组件在吸收到事件时,都足以拍卖此事件,而任因此事件是不是在上超级已拿到处
    理,还留存事件未被处理的情状。明显,那就是不纯的任务链形式,早期的Java
    AWT事件模 型(JDK 1.0及更早)中的那种事件处理机制又叫事件浮升(伊夫nt
    Bubbling)机制。从Java.1.1将来,JDK使用观看者情势代表任务链情势来处总管件。近日,在JavaScript中还是可以够接纳那种事件浮升机制来拓展事件处理。

 

呼吁的链式处理——职分链格局(三)

16.3 完整消除方案

为了让购销单的审批流程进一步灵活,并落实购买单的链式传递和处理,Sunny公司开发人士使用职务链形式来落成购买单的分别审批,其主导协会如图16-3所示:

图片 5

图16-3 购销单分级审批结构图

在图16-3中,抽象类Approver充当抽象处理者(抽象传递者),Director、VicePresident、President和Congress充当具体处理者(具体传递者),PurchaseRequest充当请求类。完整代码如下所示:

//采购单:请求类  
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;  
    }  

    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;  
    }  
}  

//审批者类:抽象处理者  
abstract class Approver {  
    protected Approver successor; //定义后继对象  
    protected String name; //审批者姓名  

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

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

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

//主任类:具体处理者  
class Director extends Approver {  
    public Director(String name) {  
        super(name);  
    }  

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

//副董事长类:具体处理者  
class VicePresident extends Approver {  
    public VicePresident(String name) {  
        super(name);  
    }  

    //具体请求处理方法  
    public void processRequest(PurchaseRequest request) {  
        if (request.getAmount() < 100000) {  
            System.out.println("副董事长" + this.name + "审批采购单:" + request.getNumber() + ",金额:" + request.getAmount() + "元,采购目的:" + request.getPurpose() + "。");  //处理请求  
        }  
        else {  
            this.successor.processRequest(request);  //转发请求  
        }     
    }  
}  

//董事长类:具体处理者  
class President extends Approver {  
    public President(String name) {  
        super(name);  
    }  

    //具体请求处理方法  
    public void processRequest(PurchaseRequest request) {  
        if (request.getAmount() < 500000) {  
            System.out.println("董事长" + this.name + "审批采购单:" + request.getNumber() + ",金额:" + request.getAmount() + "元,采购目的:" + request.getPurpose() + "。");  //处理请求  
        }  
        else {  
            this.successor.processRequest(request);  //转发请求  
        }  
    }  
}  

//董事会类:具体处理者  
class Congress extends Approver {  
    public Congress(String name) {  
        super(name);  
    }  

    //具体请求处理方法  
    public void processRequest(PurchaseRequest request) {  
        System.out.println("召开董事会审批采购单:" + request.getNumber() + ",金额:" + request.getAmount() + "元,采购目的:" + request.getPurpose() + "。");        //处理请求  
    }      
}

编制如下客户端测试代码:

class Client {  
    public static void main(String[] args) {  
        Approver wjzhang,gyang,jguo,meeting;  
        wjzhang = new Director("张无忌");  
        gyang = new VicePresident("杨过");  
        jguo = new President("郭靖");  
        meeting = new Congress("董事会");  

        //创建职责链  
        wjzhang.setSuccessor(gyang);  
        gyang.setSuccessor(jguo);  
        jguo.setSuccessor(meeting);  

        //创建采购单  
        PurchaseRequest pr1 = new PurchaseRequest(45000,10001,"购买倚天剑");  
        wjzhang.processRequest(pr1);  

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

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

        PurchaseRequest pr4 = new PurchaseRequest(800000,10004,"购买桃花岛");  
        wjzhang.processRequest(pr4);  
    }  
}

编译并运维程序,输出结果如下:

主任张无忌审批采购单:10001,金额:45000.0元,采购目的:购买倚天剑。
副董事长杨过审批采购单:10002,金额:60000.0元,采购目的:购买《葵花宝典》。
董事长郭靖审批采购单:10003,金额:160000.0元,采购目的:购买《金刚经》。
召开董事会审批采购单:10004,金额:800000.0元,采购目的:购买桃花岛。

若果急需在系统伸张一个新的有血有肉处理者,如扩张2个经营(Manager)角色可以审批5万元至8万元(不包含8万元)的买进单,需求编制多个新的切实可行处理者类Manager,作为抽象处理者类Approver的子类,实未来Approver类中定义的架空处理格局,要是买入金额当先等于8万元,则将呼吁转载给下家,代码如下所示:

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

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

出于链的创导进程由客户端负责,因而伸张新的现实处理者类对原来类库无别的影响,无须修改已有类的源代码,符合“开闭原则”。

在客户端代码中,如若要将新的切实可行请求处理者应用在系统中,必要创立新的实际处理者对象,然后将该目标插手职分链中。如在客户端测试代码中加进如下代码:

Approver rhuang;  
rhuang = new Manager("黄蓉");

将建链代码改为:

//创建职责链

//将“黄蓉”作为“张无忌”的下家
wjzhang.setSuccessor(rhuang);

//将“杨过”作为“黄蓉”的下家
rhuang.setSuccessor(gyang); 
gyang.setSuccessor(jguo);
jguo.setSuccessor(meeting);

再度编译并运营程序,输出结果如下:

主任张无忌审批采购单:10001,金额:45000.0元,采购目的:购买倚天剑。 
经理黄蓉审批采购单:10002,金额:60000.0元,采购目的:购买《葵花宝典》。
董事长郭靖审批采购单:10003,金额:160000.0元,采购目的:购买《金刚经》。 
召开董事会审批采购单:10004,金额:800000.0元,采购目的:购买桃花岛。

思考

只要将审批流程由“老总–>副董事长–>董事长–>董事会”调整为“主管–>董事长–>董事会”,系统将做出什么改观?预测修改以往客户端代码的输出结果。

职分链形式统计

义务链情势通过建立一条链来公司呼吁的处理者,请求将本着链进行传递,请求发送者无须
知道请求在何时、何处以及如何被拍卖,完成了请求发送者与处理者的解耦。在软件开发
中,假若遇上有多个对象可以拍卖同一请求时可以拔取任务链方式,例如在Web应用开发中创建一个过滤器(Filter)链来对请求数据进行过滤,在工作流系统中贯彻文件的分级审批等等,使
用义务链情势能够较好地消除此类题材。

16.1 购买销售单的分级审批

      Sunny软件公司承接了某企业SCM(Supply Chain Management,供应链管理)系统的开发任务,其中包含一个采购审批子系统。该企业的采购审批是分级进行的,即根据采购金额的不同由不同层次的主管人员来审批,主任可以审批5万元以下(不包括5万元)的采购单,副董事长可以审批5万元至10万元(不包括10万元)的采购单,董事长可以审批10万元至50万元(不包括50万元)的采购单,50万元及以上的采购单就需要开董事会讨论决定。如图16-1所示:

图16-1 采购单分级审批示意图

      怎么着在软件中落到实处购买单的独家审批?Sunny软件公司开发人士提议了壹个先河化解方案,在系统中提供三个购置单处理类PurchaseRequestHandler用于统一处理购买销售单,其框架代码如下所示:

//采购单处理类  
class PurchaseRequestHandler {  
    //递交采购单给主任  
    public void sendRequestToDirector(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) {  
        //代码省略  
    }  
}  

 

标题一般很简短,但仔细分析,发现上述方案存在如下多少个难点:

      
(1)PurchaseRequestHandler类较为庞大,各种级其余审批办法都集中在三个类中,违反了“单一职务规范”,测试和掩护难度大。

      
(2)若是急需充实一个新的审批级别或调整其余顶级的审批金额和审批细节(例如将董事长的审批额度改为60万元)时都必须修改源代码并拓展严加测试,别的,借使急需移除某超级别(例如金额为10万元及以上的采办单直接由董事长审批,不再设副董事长一职)时也必须对源代码进行改动,违反了“开闭原则”。

      
(3)审批流程的安装不够灵活性,以往的审批流程是“老总–>副董事长–>董事长–>董事会”,倘诺急需改为“老板–>董事长–>董事会”,在此方案中只好经过修改源代码来落到实处,客户端不能定制审批流程。

       怎样针对上述难题对系统举行革新?Sunny集团开发人员急切须要一种新的设计方案,万幸有任务链形式,通过动用任务链方式大家可以最大程度地化解那么些题材,下边让大家规范进入职责链情势的求学。

伸手的链式处理——任务链格局(四)

16.4 纯与不纯的天职链格局

义务链格局可分为纯的任务链情势和不纯的天职链方式三种:

(1) 纯的义务链格局

一个纯的任务链方式须求三个有血有肉处理者对象只可以在五个表现中选拔1个:要么承担任何权责,要么将义务推给下家,不允许现身某1个现实处理者对象在担负了一某个或任何职务后又将任务向下传递的场合。而且在纯的天职链方式中,须求三个伸手必须被某二个处理者对象所接到,不可以出现某些请求未被其余一个处理者对象处理的图景。在前边的购买销售单审批实例中运用的是纯的职分链格局。

(2)不纯的天职链情势

在3个不纯的任务链情势中允许某些请求被3个具体处理者部分处理后再向下传递,可能二个切实可行处理者处理完某请求后其后继处理者可以持续处理该请求,而且三个呼吁可以最后不被其它处理者对象所接到。Java
AWT
1.0中的事件处理模型应用的是不纯的义务链格局,其基本原理如下:由于窗口组件(如按钮、文本框等)一般都位居容器组件中,因而当事件暴发在某2个组件上时,先经过组件对象的handle伊夫nt()方法将事件传递给相应的事件处理方法,该事件处理方法将处理此事件,然后决定是或不是将该事件向上一流容器组件传播;上级容器组件在接收事件未来可以继承处理此事件并决定是或不是继续向上司容器组件传播,如此频仍,直到事件到达顶层容器组件截至;固然直接传到最顶层容器仍尚未处理办法,则该事件不予处理。每一级组件在收到到事件时,都可以处理此事件,而不论是此事件是或不是在上一级已收获处理,还存在事件未被拍卖的事态。鲜明,那就是不纯的天职链情势,早期的Java
AWT事件模型(JDK 1.0及更早)中的这种事件处理机制又叫事件浮升(伊芙nt
Bubbling)机制。从Java.1.1现在,JDK使用观看者格局代表职分链情势来处负责人件。最近,在JavaScript中还可以使用那种事件浮升机制来拓展事件处理。

16.5 任务链方式总括

任务链形式通过创建一条链来公司呼吁的处理者,请求将本着链举办传递,请求发送者无须知道请求在哪一天、何处以及怎样被处理,落成了请求发送者与处理者的解耦。在软件开发中,如果遇上有多少个目的足以处理同一请求时可以运用任务链形式,例如在Web应用开发中创建3个过滤器(Filter)链来对请求数据进行过滤,在工作流系统中落到实处公文的各自审批等等,使用义务链方式能够较好地解决此类难点。

1.器重优点

职分链格局的关键优点如下:

(1)
职分链方式使得八个目的并非知道是其他哪一个对象处理其请求,对象仅需清楚该请求会被拍卖即可,接收者和发送者都尚未对方的明明新闻,且链中的对象不须要知道链的结构,由客户端负责链的创制,下降了系统的耦合度。

(2)
请求处理对象仅需保障多少个针对性其后继者的引用,而不须求保持它对富有的候选处理者的引用,可简化对象的互相连接。

(3)
在给目的分派义务时,任务链可以给大家更加多的八面见光,可以通过在运作时对该链进行动态的增添或修改来充实或改动处理两个呼吁的天职。

(4)
在系统中增添贰个新的现实请求处理者时绝不修改原有系统的代码,只须要在客户端重新建链即可,从这点来看是顺应“开闭原则”的。

2.生死攸关症结

职务链情势的首要缺点如下:

(1)
由于1个呼吁没有驾驭的接收者,那么就不大概担保它必将会被拍卖,该请求或然一贯到链的末端都得不到拍卖;1个伸手也只怕因任务链没有被科学配置而得不到拍卖。

(2)
对于对比长的职务链,请求的处理或然波及到多少个处理对象,系统性能将受到一定影响,而且在拓展代码调试时不太方便。

(3) 假若建链不当,只怕会造成循环调用,将导致系统陷入死循环。

3.适用场景

在偏下情状下能够设想使用义务链格局:

(1)
有五个对象足以处理同二个请求,具体哪个目的处理该请求待运维时刻再显然,客户端只需将请求提交到链上,而无须关怀请求的处理目标是何人以及它是怎么处理的。

(2) 在不明了钦赐接收者的景色下,向多个目的中的三个提交三个伸手。

(3)
可动态钦命一组对象处理请求,客户端可以动态创造任务链来处理请求,还能转移链中处理者之间的主次次序。

练习

Sunny软件商店的OA系统需求提供1个假条审批模块:假使职工请假天数小于3天,总监可以审批该假条;如若职工请假天数大于等于3天,小于10天,高管可以审批;假若职工请假天数大于等于10天,小于30天,总CEO可以审批;假若跨越30天,总老总也不可以审批,提醒相应的不肯消息。试用义务链方式设计该假条审批模块。

训练会在我的github上做掉

任务链格局优缺点

16.2 任务链格局概述

     
很多动静下,在壹个软件系统中可以拍卖某些请求的目标不止八个,例如SCM系统中的买卖单审批,COO、副董事长、董事长和董事会都可以处理购买销售单,他们得以构成一条处理买卖单的链式结构,买卖单沿着那条链举办传递,那条链就叫做任务链。任务链可以是一条直线、三个环恐怕1个树形结构,最广泛的职分链是直线型,即沿着一条单向的链来传递请求。链上的每壹个对象都是伸手处理者,职分链格局可以将呼吁的处理者协会成一条链,并让请求沿着链传递,由链上的处理者对请求进行对应的处理,客户端无须关怀请求的拍卖细节以及呼吁的传递,只需将请求发送到链上即可,完结请求发送者和请求处理者解耦。

      职务链方式定义如下:

职责链模式(Chain of Responsibility  Pattern):避免请求发送者与接收者耦合在一起,让多个对象都有可能接收请求,将这些对象连接成一条链,并且沿着这条链传递请求,直到有对象处理它为止。职责链模式是一种对象行为型模式。

     
任务链格局协会的中坚在于引入了三个华而不实处理者。任务链格局结构如图16-2所示:

图片 6

 

 

      在职务链格局结构图中富含如下多少个剧中人物:

     
● Handler(抽象处理者):它定义了二个拍卖请求的接口,一般设计为抽象类,由于不一致的现实处理者处理请求的主意分裂,由此在中间定义了望梅止渴请求处理措施。因为每三个处理者的下家照旧3个处理者,由此在空洞处理者中定义了三个虚幻处理者类型的目的(如协会图中的successor),作为其对下家的引用。通过该引用,处理者可以连成一条链。

     
 ConcreteHandler(具体处理者):它是聊以自慰处理者的子类,可以拍卖用户请求,在实际处理者类中贯彻了聊以自慰处理者中定义的虚幻请求处理措施,在拍卖请求之前须要举办判定,看是还是不是有照应的拍卖权限,若是得以处理请求就处理它,否则将请求转载给后继者;在实际处理者中得以访问链中下七个目的,以便请求的转折。

     
在职务链格局里,很多对象由每二个目的对其下家的引用而连接起来形成一条链。请求在这几个链上传递,直到链上的某1个目标说了算拍卖此恳请。发出这些请求的客户端并不知道链上的哪二个目标最终处理这一个请求,那使得系统可以在不影响客户端的景观下动态地再次社团链和分配责任

     
职分链形式的主导在于抽象处理者类的规划,抽象处理者的卓著代码如下所示:

abstract class Handler {  
    //维持对下家的引用  
protected Handler successor;  

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

    public abstract void handleRequest(String request);  
}  

上述代码中,抽象处理者类定义了对下家的引用对象,以便将呼吁转载给下家,该目标的访问符可设为protected,在其子类中可以运用。在空虚处理者类中宣示了纸上谈兵的呼吁处理措施,具体落到实处交由子类完结。

       
具体处理者是架空处理者的子类,它具备两大效益:率先是处理请求,差其余切实处理者以不相同的款式落成抽象请求处理办法handleRequest();其次是转账呼吁,若是该请求超出了如今处理者类的权力,可以将该请求转发给下家。具体处理者类的出众代码如下:

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

  在切切实实处理类中经过对请求举办判定可以做出相应的拍卖。

        须求小心的是,职责链方式并不创建职务链,职分链的创制工作必须由系统的其余一些来成功,一般是在运用该任务链的客户端中创建义务链。职务链方式降低了请求的发送端和接收端之间的耦合,使几个目标都有机会处理这几个请求。 

 

思考

如何在客户端创建一条职责链?

 

优点:

  • 任务链情势使得一个对象并非知道是任何哪贰个目的处理其请求,对象仅需了解该请求会
    被处理即可,接收者和发送者都不曾对方的醒目音信,且链中的对象不需求知道链的布局,
    由客户端负责链的创立,降低了系统的耦合度。
  • 恳请处理目的仅需保证三个针对其后继者的引用,而不必要保证它对具有的候选处理者的
    引用,可简化对象的相互连接。
  • 在给目标分派职务时,职分链可以给大家越多的八面见光,能够经过在运作时对该链举行动
    态的加码或修改来充实或转移处理1个呼吁的天职。
  • 在系统中加进一个新的有血有肉请求处理者时决不修改原有系统的代码,只必要在客户端重新
    建链即可,从那点来看是契合“开闭原则”的。

16.3 完整化解方案

      为了让购买销售单的审批流程进一步灵敏,并促成买卖单的链式传递和拍卖,Sunny公司开发人士使用任务链格局来贯彻购销单的各自审批,其主题构造如图16-3所示:

图片 7

       在图16-3中,抽象类Approver充当抽象处理者(抽象传递者),Director、VicePresident、President和Congress充当具体处理者(具体传递者),PurchaseRequest充当请求类。完整代码如下所示:

//采购单:请求类  
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;  
    }  

    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;  
    }  
}  

//审批者类:抽象处理者  
abstract class Approver {  
    protected Approver successor; //定义后继对象  
    protected String name; //审批者姓名  

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

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

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

//主任类:具体处理者  
class Director extends Approver {  
    public Director(String name) {  
        super(name);  
    }  

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

//副董事长类:具体处理者  
class VicePresident extends Approver {  
    public VicePresident(String name) {  
        super(name);  
    }  

    //具体请求处理方法  
    public void processRequest(PurchaseRequest request) {  
        if (request.getAmount() < 100000) {  
            System.out.println("副董事长" + this.name + "审批采购单:" + request.getNumber() + ",金额:" + request.getAmount() + "元,采购目的:" + request.getPurpose() + "。");  //处理请求  
        }  
        else {  
            this.successor.processRequest(request);  //转发请求  
        }     
    }  
}  

//董事长类:具体处理者  
class President extends Approver {  
    public President(String name) {  
        super(name);  
    }  

    //具体请求处理方法  
    public void processRequest(PurchaseRequest request) {  
        if (request.getAmount() < 500000) {  
            System.out.println("董事长" + this.name + "审批采购单:" + request.getNumber() + ",金额:" + request.getAmount() + "元,采购目的:" + request.getPurpose() + "。");  //处理请求  
        }  
        else {  
            this.successor.processRequest(request);  //转发请求  
        }  
    }  
}  

//董事会类:具体处理者  
class Congress extends Approver {  
    public Congress(String name) {  
        super(name);  
    }  

    //具体请求处理方法  
    public void processRequest(PurchaseRequest request) {  
        System.out.println("召开董事会审批采购单:" + request.getNumber() + ",金额:" + request.getAmount() + "元,采购目的:" + request.getPurpose() + "。");        //处理请求  
    }      
}  

 编写如下客户端测试代码: 

class Client {  
    public static void main(String[] args) {  
        Approver wjzhang,gyang,jguo,meeting;  
        wjzhang = new Director("张无忌");  
        gyang = new VicePresident("杨过");  
        jguo = new President("郭靖");  
        meeting = new Congress("董事会");  

        //创建职责链  
        wjzhang.setSuccessor(gyang);  
        gyang.setSuccessor(jguo);  
        jguo.setSuccessor(meeting);  

        //创建采购单  
        PurchaseRequest pr1 = new PurchaseRequest(45000,10001,"购买倚天剑");  
        wjzhang.processRequest(pr1);  

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

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

        PurchaseRequest pr4 = new PurchaseRequest(800000,10004,"购买桃花岛");  
        wjzhang.processRequest(pr4);  
    }  
}   

编译并运营程序,输出结果如下:

主任张无忌审批采购单:10001,金额:45000.0元,采购目的:购买倚天剑。

副董事长杨过审批采购单:10002,金额:60000.0元,采购目的:购买《葵花宝典》。

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

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

     
假诺必要在系统扩张一个新的切实处理者,如增添三个经营(Manager)角色能够审批5万元至8万元(不包括8万元)的买入单,需求编制2个新的现实性处理者类Manager,作为抽象处理者类Approver的子类,实今后Approver类中定义的抽象处理格局,若是购买金额当先等于8万元,则将呼吁转发给下家,代码如下所示:

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

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

 由于链的始建进度由客户端负责,因而增添新的切实可行处理者类对原本类库无其余影响,无须修改已有类的源代码,符合“开闭原则”。

     
在客户端代码中,假设要将新的现实性请求处理者应用在系统中,须求创设新的切实可行处理者对象,然后将该目标参与义务链中。如在客户端测试代码中伸张如下代码:

Approver rhuang;  
rhuang = new Manager("黄蓉"); 

将建链代码改为:

//创建职责链  
wjzhang.setSuccessor(rhuang); //将“黄蓉”作为“张无忌”的下家  
rhuang.setSuccessor(gyang); //将“杨过”作为“黄蓉”的下家  
gyang.setSuccessor(jguo);  
jguo.setSuccessor(meeting);  

再一次编译并运行程序,输出结果如下:

主任张无忌审批采购单:10001,金额:45000.0元,采购目的:购买倚天剑。

经理黄蓉审批采购单:10002,金额:60000.0元,采购目的:购买《葵花宝典》。

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

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

 

 

思考

       如果将审批流程由“主任–>副董事长–>董事长–>董事会”调整为“主任–>董事长–>董事会”,系统将做出哪些改动?预测修改之后客户端代码的输出结果。

 

缺点:

  • 鉴于一个伸手没有明显的收信人,那么就不可以确保它必然会被处理,该请求大概直接到链
    的前边都得不遍地理;三个请求也大概因职分链没有被正确配置而得不各处理。
  • 对于比较长的职分链,请求的处理或许波及到七个处理对象,系统品质将面临一定影响,
    而且在进展代码调试时不太方便。
  • 一旦建链不当,只怕会促成循环调用,将导致系统陷入死循环。

16.4 纯与不纯的天职链方式

      义务链格局可分为纯的天职链形式和不纯的天职链情势二种:

 

       (1) 纯的天职链方式

     
三个纯的天职链形式须求多少个有血有肉处理者对象只可以在两个表现中选取贰个:抑或承担全体职分,要么将任务推给下家,不允许出现某三个实际处理者对象在承担了一有个别或任何权利后又将义务向下传递的景况。而且在纯的职务链形式中,须要一个呼吁必须被某二个处理者对象所收受,无法出现有个别请求未被其它3个处理者对象处理的意况。在前头的进货单审批实例中应用的是纯的任务链格局。

 

       (2)不纯的职务链形式

     
在三个不纯的义务链形式中同意某些请求被三个实际处理者部分处理后再向下传递,恐怕1个现实处理者处理完某请求后其后继处理者可以接二连三处理该请求,而且二个请求可以最终不被其余处理者对象所拔取Java AWT
1.0中的事件处理模型应用的是不纯的义务链形式,其基本原理如下:由于窗口组件(如按钮、文本框等)一般都放在容器组件中,因而当事件发生在某多个组件上时,先通过组件对象的handle伊芙nt()方法将事件传递给相应的事件处理方法,该事件处理方法将拍卖此事件,然后决定是还是不是将该事件向上拔尖容器组件传播;上级容器组件在接到事件过后可以一而再处理此事件并操纵是还是不是持续向上级容器组件传播,如此反复,直到事件到达顶层容器组件为止;借使直白传到最顶层容器仍尚未处理办法,则该事件不予处理。每顶尖组件在吸纳到事件时,都得以拍卖此事件,而无论是此事件是还是不是在上一流已取得处理,还留存事件未被处理的情形。鲜明,那就是不纯的天职链情势,早期的Java AWT事件模型(JDK
1.0及更早)中的那种事件处理机制又叫事件浮升(伊夫nt
Bubbling)机制
。从Java.1.1以往,JDK使用观看者格局代表义务链形式来处负责人件。近日,在JavaScript中还可以够动用那种事件浮升机制来开展事件处理。

 

适用场景

  • 有多少个对象可以拍卖同2个呼吁,具体哪些目的处理该请求待运营时刻再鲜明,客户端只
    需将请求提交到链上,而无须关怀请求的处理目标是哪个人以及它是怎么处理的。
  • 在不显眼钦点接收者的气象下,向五个对象中的3个交由3个呼吁。
  • 可动态指定一组对象处理请求,客户端可以动态成立职分链来处理请求,还是可以改变链中
    处理者之间的主次次序。

16.5 任务链情势计算

      职分链方式通过确立一条链来公司请求的处理者,请求将沿着链举办传递,请求发送者无须知道请求在几时、何处以及哪些被处理,完毕了请求发送者与处理者的解耦。在软件开发中,假使遭遇有八个对象可以拍卖同一请求时可以采用职分链情势,例如在Web应用开发中开创一个过滤器(Filter)链来对请求数据开展过滤,在工作流系统中贯彻文件的分级审批等等,使用义务链情势可以较好地化解此类题材。

 

       1.要害优点

      义务链方式的关键优点如下:

      
(1) 职务链形式使得七个对象并非知道是其余哪一个对象处理其请求,对象仅需清楚该请求会被处理即可,接收者和发送者都没有对方的显著音信,且链中的对象不须要知道链的结构,由客户端负责链的创始,下降了系统的耦合度。

      
(2) 请求处理目标仅需保证二个针对其后继者的引用,而不须要保证它对持有的候选处理者的引用,可简化对象的互相连接。

      
(3) 在给目的分派义务时,任务链可以给大家更多的油滑,可以经过在运营时对该链进行动态的加码或涂改来充实或转移处理3个请求的义务。

      
(4) 在系统中加进多个新的实际请求处理者时不用修改原有系统的代码,只须求在客户端重新建链即可,从那一点来看是相符“开闭原则”的。

      

       2.关键缺点

      任务链形式的第二缺点如下:

      
(1) 由于一个伸手没有精通的收信人,那么就不只怕保险它一定会被拍卖,该请求或许一向到链的前面都得不到拍卖;贰个呼吁也或许因义务链没有被科学配置而得不到拍卖。

      
(2) 对于比较长的任务链,请求的处理只怕波及到多少个处理目标,系统品质将倍受肯定影响,而且在展开代码调试时不太方便。

       (3) 如若建链不当,或许会促成循环调用,将导致系统陷入死循环。

 

       3.适用场景

      在以下情状下得以设想动用义务链格局:

      
(1) 有五个目的足以处理同八个伸手,具体哪个目的处理该请求待运营时刻再明确,客户端只需将请求提交到链上,而无须关注请求的拍卖对象是哪个人以及它是怎么处理的。

       (2) 在不分明指定接收者的景况下,向三个目的中的三个交付2个伸手。

     
  (3) 可动态内定一组对象处理请求,客户端可以动态成立义务链来处理请求,还足以变动链中处理者之间的先后次序。 

 

 

练习

       Sunny软件公司的OA系统需要提供一个假条审批模块:如果员工请假天数小于3天,主任可以审批该假条;如果员工请假天数大于等于3天,小于10天,经理可以审批;如果员工请假天数大于等于10天,小于30天,总经理可以审批;如果超过30天,总经理也不能审批,提示相应的拒绝信息。试用职责链模式设计该假条审批模块。

 

 

【作者:刘伟 http://blog.csdn.net/lovelion

相关文章