最新公告
  • 新注册用户请前往个人中心绑定邮箱以便接收相关凭证邮件!!!点击前往个人中心
  • 设计模式之职责链

    职责链(Chain of Responsibility)模式属于23种设计模式之一,职责链也称为责任链,《Design pattern: the basis of reusable object-oriented software》(以下简称DP)一书中是这样描述职责链的:职责链模式使多个对象都有机会处理请求,从而避免请求发送者和接收者之间的耦合关系。将这个对象连成一条链,并沿这条链传递该请求,直到有一个对象处理它为止。
    DP中对职责链模式的定义稍微不是那么的好理解,简单来说就是将能够处理用户请求的对象都串成一条链,然后将用户的请求放进这条链里,这个请求就可以在链中的对象之间传递,一直传递到能够处理它的对象上为止。
    可能这么说还不太好理解,我们可以举个生活中的例子,例如员工申请加薪。我们都知道普通员工想要申请加薪,首先需要将加薪的申请书提交给主管,如果主管没有权限处理这个加薪的事情,那么主管就会将申请提交到部门经理那边,由部门经理去处理。如果部门经理也没有权处理的话,就会将申请再提交到总经理或人力资源总监手上,总经理说我可以处理,那么这个加薪申请就可以被处理了。而普通员工并不知道他提交的这个加薪申请被谁处理了,也不知道处理的经过,他只需要知道将申请提交给主管,最后等结果即可。这就是一个典型的职责链机制:客户端发送请求到职责链的链头上,然后这个请求就会在链中的对象之间逐个传递,直到被某个对象处理并返回结果给客户端为止。
    示意图:
    不使用职责连模式设计的代码:
    就拿以上所说到的例子,我们来用代码做一个简单的试验,先不使用职责链模式,就用最简单方式去实现这个场景。
    1.编写一个RaisesRequest类,用来封装请求内容:
    package org.zero01.test;publicclassRaisesRequest{

       public String getRequestName(){
           return requestName;
       }

       publicvoidsetRequestName(String requestName){
           this.requestName = requestName;
       }

       publicintgetRequestNumber(){
           return requestNumber;
       }

       publicvoidsetRequestNumber(int requestNumber){
           this.requestNumber = requestNumber;
       }

       private String requestName;
       privateint requestNumber;

    }

    2.编写管理者类Manager,这个类用于处理用户的请求:
    publicclassManager {

       
       publicvoidgetResult(String position, RaisesRequest raisesRequest) {

           if (position.equals(“主管”)) {

               if (raisesRequest.getRequestName().equals(“加薪”)) {
                   System.out.println(“主管:我无权处理”);
               } else {
                   System.out.println(“主管:你在说啥”);
               }

           } elseif (position.equals(“部门经理”)) {

               if (raisesRequest.getRequestName().equals(“加薪”)) {
                   System.out.println(“部门经理:我无权处理”);
               } else {
                   System.out.println(“部门经理:你在说啥”);
               }

           } elseif (position.equals(“人力资源总监”)) {

               if (raisesRequest.getRequestName().equals(“加薪”)) {
                   System.out.println(“人力资源总监:我无权处理”);
               } else {
                   System.out.println(“人力资源总监:你在说啥”);
               }

           } elseif (position.equals(“总经理”)) {

               if (raisesRequest.getRequestName().equals(“加薪”) && raisesRequest.getRequestNumber() <= 1000) {
                   System.out.println(“总经理:ok,批准了”);
               } elseif(raisesRequest.getRequestName().equals(“加薪”) && raisesRequest.getRequestNumber() > 1000){
                   System.out.println(“总经理:emmm我考虑一下”);
               }else{
                   System.out.println(“总经理:你在说啥”);
               }

           } else {
               System.out.println(“没有这个职位”);
           }
       }
    }

    3.最后编写客户端类Client:
    package org.zero01.test;
    publicclassClient{

       publicstaticvoidmain(String[] args){

           
           RaisesRequest raisesRequest = new RaisesRequest();
           raisesRequest.setRequestName(“加薪”);
           raisesRequest.setRequestNumber(1000);

           
           Manager manager = new Manager();
           manager.getResult(“主管”, raisesRequest);
           manager.getResult(“部门经理”, raisesRequest);
           manager.getResult(“人力资源总监”, raisesRequest);
           manager.getResult(“总经理”, raisesRequest);

       }

    }

    运行结果:
    主管:我无权处理 部门经理:我无权处理 人力资源总监:我无权处理 总经理:ok,批准了
    ok,从运行结果也可以看到,这样的代码也是勉勉强强实现了我们的需求。但是,很直观的可以看到代码是有多糟糕,Manager类里的if else全都集中在一个方法里,造成一个方法里重复的代码太多。而且不同部门的管理者都在这个方法里,这就违反了单一职责原则,如果我要增加一个部门的管理者或减少一个部门的管理者,都需要去修改这个类里的代码以及客户端的代码,违反了开-闭原则。这样的代码耦合性是相当高的,但是我们要怎么样对这些代码进行解耦呢?那么这时候就需要使用到职责链模式了。
    职责链模式结构图:
    职责连模式示例代码:
    1.Handler类,定义一个处理请求的接口:
    package org.zero01.test;
    publicabstractclassHandler{

       protected Handler successor;

       
       publicvoidsetSuccessor(Handler successor){
           this.successor = successor;
       }

       publicabstractvoidhandlerRequest(int request);

    }

    2.ConcreteHandler1类,是一个具体的处理类,它只能处理它所负责的请求,不能处理的就传递给它的后继者处理:
    package org.zero01.test;
    publicclassConcreteHandler1extendsHandler{

       publicvoidhandlerRequest(int request){
           
           if (request >= 0 && request <= 10) {
               System.out.println(“ConcreteHandler1处理请求” + request);
           } elseif (successor != null) {
               
               successor.handlerRequest(request);
           }
       }

    }

    3.ConcreteHandler2:
    package org.zero01.test;
    publicclassConcreteHandler2extendsHandler{

       publicvoidhandlerRequest(int request){
           
           if (request >= 10 && request <= 20) {
               System.out.println(“ConcreteHandler2处理请求” + request);
           } elseif (successor != null) {
               
               successor.handlerRequest(request);
           }
       }
    }

    4.ConcreteHandler3:
    package org.zero01.test;
    publicclassConcreteHandler3extendsHandler{

       publicvoidhandlerRequest(int request){
           
           if (request >= 20 && request <= 30) {
               System.out.println(“ConcreteHandler3处理请求” + request);
           } elseif (successor != null) {
               
               successor.handlerRequest(request);
           }
       }

    }

    5.Client,客户端类,在该类中向职责链提交请求:
    package org.zero01.test;
    publicclassClient{

       publicstaticvoidmain(String[] args){

           Handler h1 = new ConcreteHandler1();
           Handler h2 = new ConcreteHandler2();
           Handler h3 = new ConcreteHandler3();

           
           h1.setSuccessor(h2);
           h2.setSuccessor(h3);

           int[] numbers = {10, 20, 30, 51, 25, 5, 8, 7, 15, 47};

           for (int i : numbers) {
               h1.handlerRequest(i);
           }
       }
    }
    运行结果:
    ConcreteHandler1处理请求10 ConcreteHandler2处理请求20 ConcreteHandler3处理请求30 ConcreteHandler3处理请求25 ConcreteHandler1处理请求5 ConcreteHandler1处理请求8 ConcreteHandler1处理请求7 ConcreteHandler2处理请求15
    职责链的好处:
    从以上这个小示例可以看到,每当我从客户端提交一个请求时,请求是沿着职责链传递的,直至传递到有对应的ConcreteHandler对象来处理它。请求的处理着与发送者都不需要知道对方的明确信息,且链中的对象自己也并不知道链的结构。所以职责链可以简化对象之间的互相连接,它们仅需要保持一个指向其后继者的引用,而不需要保持它所有的候选者的引用,从这点也可以看出职责链是单链的结构,这样大大的降低了类之间的耦合度,因为它们都只依赖于一个抽象父类。
    类之间低耦合的好处就是可以随时增加、减少以及修改某个处理请求的结构代码,增强了给对象指派职责的灵活性。但是要注意一个请求可能到了链的末端都得不到处理,或者因为没有正确配置而得不到处理。这时候估计就有人注意到以上代码的一个小细节,数值大于30的请求都没有被处理,这就是典型的请求被传递到了链的末端也得不到处理。所以在设计代码时还需要仔细考虑全面,如果是上面这个例子我们就可以简单的加一个else分支来处理这个问题,而不同的场景下需要根据实际情况使用相应的方式去处理。
    使用职责连模式重构之前的代码:
    我们顺便把需求更改一下,让不同的管理者可以处理不同额度的加薪请求。
    代码结构图:
    1.抽象一个Manager管理类:
    package org.zero01.test;
    publicabstractclassManager{

       protected Manager superior;
       protected String position;

       
       publicManager(String position){
           this.position = position;
       }

       
       publicvoidsetSuperior(Manager superior){
           this.superior = superior;
       }

       
       publicabstractvoidraisesRequest(String requestName,int requestNumber);

    }

    2.编写主管类,这是具体的处理类,可以处理100元以内的加薪请求:
    package org.zero01.test;
    publicclassDirectorextendsManager{

       publicDirector(String position){
           super(position);
       }

       publicvoidraisesRequest(String requestName, int requestNumber){

           
           if (requestName.equals(“加薪”) && requestNumber <= 100) {
               System.out.println(position + “:ok,批准了”);
           } elseif (superior != null) {
               
               superior.raisesRequest(requestName, requestNumber);
           }

       }
    }

    3.部门经理类,可以处理300元以内的加薪请求:
    package org.zero01.test;
    publicclassBranchManagerextendsManager{

       publicBranchManager(String position){
           super(position);
       }

       publicvoidraisesRequest(String requestName, int requestNumber){

           
           if (requestName.equals(“加薪”) && requestNumber <= 300) {
               System.out.println(position + “:ok,批准了”);
           } elseif (superior != null) {

               
               superior.raisesRequest(requestName, requestNumber);
           }
       }

    }

    4.人力资源总监类,可以处理600元以内的加薪请求:
    package org.zero01.test;
    publicclassCHOextendsManager{

       publicCHO(String position){
           super(position);
       }

       publicvoidraisesRequest(String requestName, int requestNumber){

           
           if (requestName.equals(“加薪”) && requestNumber <= 600) {
               System.out.println(position + “:ok,批准了”);
           } elseif (superior != null) {

               
               superior.raisesRequest(requestName, requestNumber);
           }
       }
    }

    5.总经理类,可以处理1000元以上的加薪请求:
    package org.zero01.test;
    publicclassGeneralManagerextendsManager{

       publicGeneralManager(String position){
           super(position);
       }

       publicvoidraisesRequest(String requestName, int requestNumber){

           
           if (requestName.equals(“加薪”) && requestNumber <= 1000) {
               System.out.println(position + “:ok,批准了”);
           }else {
               
               System.out.println(position + “:emmm我考虑一下”);
           }
       }

    }

    6.员工类,也就是客户端类:
    package org.zero01.test;
    publicclassStaff{

       publicstaticvoidmain(String[] args){

           
           Manager director = new Director(“主管”);
           Manager branchManager = new BranchManager(“部门经理”);
           Manager cho = new CHO(“人力资源总监”);
           Manager generalManager = new GeneralManager(“总经理”);

           
           director.setSuperior(branchManager);
           branchManager.setSuperior(cho);
           cho.setSuperior(generalManager);

           director.raisesRequest(“加薪”,100);
           director.raisesRequest(“加薪”,300);
           director.raisesRequest(“加薪”,600);
           director.raisesRequest(“加薪”,1000);
           director.raisesRequest(“加薪”,1500);

       }
    }

    运行结果:
    主管:ok,批准了 部门经理:ok,批准了 人力资源总监:ok,批准了 总经理:ok,批准了 总经理:emmm我考虑一下
    使用职责链模式来重构之前的代码后,就很好的解决了原来大量的分支判断耦合在一起,造成维护困难、灵活性差且不好扩展的问题。

    本站所有文章均由网友分享,仅用于参考学习用,请勿直接转载,如有侵权,请联系网站客服删除相关文章。若由于商用引起版权纠纷,一切责任均由使用者承担
    极客文库 » 设计模式之职责链

    常见问题FAQ

    如果资源链接失效了怎么办?
    本站用户分享的所有资源都有自动备份机制,如果资源链接失效,请联系本站客服QQ:2580505920更新资源地址。
    如果用户分享的资源与描述不符怎么办?
    可以联系客服QQ:2580505920,如果要求合理可以安排退款或者退赞助积分。
    如何分享个人资源获取赞助积分或其他奖励?
    本站用户可以分享自己的资源,但是必须保证资源没有侵权行为。点击个人中心,根据操作填写并上传即可。资源所获收益完全归属上传者,每周可申请提现一次。
    如果您发现了本资源有侵权行为怎么办?
    及时联系客服QQ:2580505920,核实予以删除。

    参与讨论

    • 169会员总数(位)
    • 3735资源总数(个)
    • 1本周发布(个)
    • 0 今日发布(个)
    • 443稳定运行(天)

    欢迎加入「极客文库」,成为原创作者从这里开始!

    立即加入 了解更多
    成为赞助用户享有更多特权立即升级