职责链模式

责任链模式定义如下:(使多个对象都有机会处理请求,从而避免了请求的发送者和接受者之间的耦合关
系。将这些对象连成一条链,并沿着这条链传递该请求,直到有对象处理它为止。)
责任链模式的重点是在“链”上,由一条链去处理相似的请求在链中决定谁来处理这个请
求,并返回相应的结果,其通用类图如图所示:


image.png

责任链模式的优点:责任链模式非常显著的优点是将请求和处理分开。请求者可以不用知道是谁处理的,处
理者可以不用知道请求的全貌(例如在J2EE项目开发中,可以剥离出无状态Bean由责任链处
理),两者解耦,提高系统的灵活
责任链模式的缺点:责任链有两个非常显著的缺点:一是性能问题,每个请求都是从链头遍历到链尾,特别
是在链比较长的时候,性能是一个非常大的问题。二是调试不很方便,特别是链条比较长,
环节比较多的时候,由于采用了类似递归的方式,调试的时候逻辑可能比较复杂。
责任链模式的注意事项:链中节点数量需要控制,避免出现超长链的情况,一般的做法是在Handler中设置一个
最大节点数量,在setNext方法中判断是否已经是超过其阈值,超过则不允许该链建立,避免
无意识地破坏系统性能
实践:
责任链在实际的项目中使用也是比较多的,我曾经做过这样一个项目,界面上有一个用
户注册功能,注册用户分两种,一种是VIP用户,也就是在该单位办理过业务的,一种是普
通用户,一个用户的注册要填写一堆信息,VIP用户只比普通用户多了一个输入项:VIP序列
号。注册后还需要激活,VIP和普通用户的激活流程也是不同的,VIP是自动发送邮件到用户
的邮箱中就算激活了,普通用户要发送短信才能激活,为什么呢?获得手机号码以后好发广
告短信啊!项目组就采用了责任链模式,甭管从前台传递过来的是VIP用户信息还是普通用
户信息,统一传递到一个处理入口通过责任链来完成任务的处理,类图如图16-5所示


image.png

实践一:
购买请求决策项目介绍
决策因素:价格
决策级别:组长,部长,副总,总裁
考虑扩展性
代码实现:

package 职责链模式;

public abstract class Approver {

    Approver successor;
    String name;
    public Approver(String name){
        this.name = name;
    }
    
    public abstract void processRequest(PurchaseRequest purchaseRequest);
    public void setSuccessor(Approver successor) {
        this.successor = successor;
    }
    
    
    
}
package 职责链模式;

public class Client {

    public Client(){}
    public PurchaseRequest sendRequest(int type,int number,float price){
        return new PurchaseRequest(type,number,price);
    }
    
}
package 职责链模式;
/**部门*/
public class DepartmentApprover extends Approver{

    public DepartmentApprover(String name){
        super(name+"DepartmentLeader");
    }

    @Override
    public void processRequest(PurchaseRequest purchaseRequest) {
        
        if (5000 <= purchaseRequest.getSum() && purchaseRequest.getSum()<10000) {
            System.out.println("**This request "+purchaseRequest.getId()+" will be handled by "+this.name+" **");
        } else {
            successor.processRequest(purchaseRequest);
        }
        
    }
    
    
}
package 职责链模式;
/**组长*/
public class GroupApprover extends Approver{

    public GroupApprover(String name){
        super(name+"GroupLeader");
    }

    @Override
    public void processRequest(PurchaseRequest purchaseRequest) {
        
        if (purchaseRequest.getSum()<5000) {
            System.out.println("**This request "+purchaseRequest.getId()+" will be handled by "+this.name+" **");
        } else {
            successor.processRequest(purchaseRequest);
        }
        
    }
    
    
}
package 职责链模式;

public class MainTest {

    public static void main(String[] args) {
        Client mClient = new Client();
        Approver group = new GroupApprover("Tom");
        Approver dept = new DepartmentApprover("Jerry");
        Approver vice = new VicePresidentApprover("Kate");
        Approver president = new PresidentApprover("Bush");
        group.setSuccessor(dept);
        dept.setSuccessor(vice);
        vice.setSuccessor(president);
        president.setSuccessor(group);
        
        group.processRequest(mClient.sendRequest(1, 100, 40));
        group.processRequest(mClient.sendRequest(2, 200, 40));
        group.processRequest(mClient.sendRequest(3, 300, 40));
        group.processRequest(mClient.sendRequest(4, 400, 140));
    }
    
}

package 职责链模式;
/**老总*/
public class PresidentApprover extends Approver{

    public PresidentApprover(String name){
        super(name+"President");
    }

    @Override
    public void processRequest(PurchaseRequest purchaseRequest) {
        
        if (50000<=purchaseRequest.getSum()) {
            System.out.println("**This request "+purchaseRequest.getId()+" will be handled by "+this.name+" **");
        } else {
            successor.processRequest(purchaseRequest);
        }
        
    }
    
    
}
package 职责链模式;

//购买请求
public class PurchaseRequest {

    private int type=0;
    private int number = 0;
    private float price=0;
    private int id=0;
    
    public PurchaseRequest(int type,int number,float price){
        this.type = type;
        this.number = number;
        this.price = price;
    }

    public int getType() {
        return type;
    }

    public void setType(int type) {
        this.type = type;
    }

    public int getNumber() {
        return number;
    }

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

    public float getPrice() {
        return price;
    }

    public void setPrice(float price) {
        this.price = price;
    }

    public int getId() {
        return (int)(Math.random()*1000);
    }

    public void setId(int id) {
        this.id = id;
    }
    //计算总和
    public float getSum(){
        return number*price;
    }
    
    
    
    
}
package 职责链模式;
/**副总*/
public class VicePresidentApprover extends Approver{

    public VicePresidentApprover(String name){
        super(name+"ViceLeader");
    }

    @Override
    public void processRequest(PurchaseRequest purchaseRequest) {
        
        if (10000<=purchaseRequest.getSum()&&purchaseRequest.getSum()<50000) {
            System.out.println("**This request "+purchaseRequest.getId()+" will be handled by "+this.name+" **");
        } else {
            successor.processRequest(purchaseRequest);
        }
        
    }
    
    
}

业务场景二:
申请聚餐费用
来考虑这样一个功能:申请聚餐费用的管理。
很多公司都有这样的福利,就是项目组或者是部门可以向公司申请一些聚餐费用,用于组织项目组成员或者是部门成员进行聚餐活动,以增进人员之间的情感,更有利于工作中的相互合作。
申请聚餐费用的大致流程一般是:由申请人先填写申请单,然后交给领导审查,如果申请批准下来了,领导会通知申请人审批通过,然后申请人去财务核领费用,如果没有核准,领导会通知申请人审批未通过,此事也就此作罢了。
不同级别的领导,对于审批的额度是不一样的,比如:项目经理只能审批500元以内的申请;部门经理能审批1000元以内的申请;而总经理可以审核任意额度的申请。
也就是说,当某人提出聚餐费用申请的请求后,该请求会由项目经理、部门经理、总经理之中的某一位领导来进行相应的处理,但是提出申请的人并不知道最终会由谁来处理他的请求,一般申请人是把自己的申请提交给项目经理,或许最后是由总经理来处理他的请求,但是申请人并不知道应该由总经理来处理他的申请请求。
那么该怎样实现这样的功能呢?


image.png

1:定义职责的抽象类
首先来看看定义所有职责的抽象类,也就是所有职责的外观,在这个类里面持有下一个处理请求的对象,同时还要定义业务处理方法,示例代码如下:

package 职责链模式2;

public abstract class Handler {

    /**
     * 持有下一个处理请求的对象
     */
    protected Handler successor = null;
    /**
     * 设置下一个处理请求的对象
     * @param successor 下一个处理请求的对象
     */
    public void setSuccessor(Handler successor){
       this.successor = successor;
    }
    
    /**
     * 处理聚餐费用的申请
     * @param user 申请人
     * @param fee 申请的钱数
     * @return 成功或失败的具体通知
     */
    public abstract String handleFeeRequest(String user,double fee);
    
}

(2)实现各自的职责
现在实现的处理聚餐费用流程是:申请人提出的申请交给项目经理处理,项目经理的处理权限是500元以内,超过500元,把申请转给部门经理处理,部门经理的处理权限是1000元以内,超过1000元,把申请转给总经理处理。
分析上述流程,对请求主要有三个处理环节,把它们分别实现成为职责对象,一个对象实现一个环节的处理功能,这样就会比较简单。
先看看项目经理的处理吧,示例代码如下:

package 职责链模式2;

public class ProjectManager extends Handler{

    @Override
    public String handleFeeRequest(String user, double fee) {
        String str = "";
        //项目经理的权限比较小,只能在500以内
        if (fee < 500) {
            if ("小小".equals(user)) {
                str = "项目经理同意"+user+"聚餐费用"+fee+"元的请求";
            } else {
                //其它人一律不同意
                  str = "项目经理不同意"+user+"聚餐费用"+fee+"元的请求";
            }
             return str;
        } else {
             //超过500,继续传递给级别更高的人处理
               if(this.successor!=null){
                  return successor.handleFeeRequest(user, fee);
               }
        }
        return str;
    }

}

接下来看看部门经理的处理,示例代码如下:

package 职责链模式2;


public class DepManager extends Handler{ 
    
    public String handleFeeRequest(String user, double fee) {
       String str = "";
       //部门经理的权限只能在1000以内
       if(fee < 1000){
           //为了测试,简单点,只同意小李申请的
           if("小小".equals(user)){
              str = "部门经理同意"+user+"聚餐费用"+fee+"元的请求";
           }else{
              //其它人一律不同意
              str = "部门经理不同意"+user+"聚餐费用"+fee+"元的请求";
           }
           return str;
       }else{
           //超过1000,继续传递给级别更高的人处理
           if(this.successor!=null){
              return this.successor.handleFeeRequest(user, fee);
           }
       }
       return str;
    }
}

再看总经理的处理,示例代码如下:

public class GeneralManager extends Handler{
    public String handleFeeRequest(String user, double fee) {
       String str = "";
       //总经理的权限很大,只要请求到了这里,他都可以处理
       if(fee >= 1000){
           //为了测试,简单点,只同意小李的
           if("小小".equals(user)){
              str = "总经理同意"+user+"聚餐费用"+fee+"元的请求";
           }else{
              //其它人一律不同意
              str = "总经理不同意"+user+"聚餐费用"+fee+"元的请求";
           }
           return str;
       }else{
           //如果还有后继的处理对象,继续传递
           if(this.successor!=null){
              return successor.handleFeeRequest(user, fee);
           }
       }
       return str;
    }
}

使用职责链
那么客户端如何使用职责链呢,最重要的就是要先构建职责链,然后才能使用。示例代码如下:

package 职责链模式2;


public class Client {
    public static void main(String[] args) {
       //先要组装职责链   
       Handler h1 = new GeneralManager();
       Handler h2 = new DepManager();
       Handler h3 = new ProjectManager();
       h3.setSuccessor(h2);
       h2.setSuccessor(h1);
      
       //开始测试
       String ret1 = h3.handleFeeRequest("小小", 300);
       System.out.println("the ret1="+ret1); 
       String ret2 = h3.handleFeeRequest("小张", 300);
       System.out.println("the ret2="+ret2); 
      
       String ret3 = h3.handleFeeRequest("小小", 600);
       System.out.println("the ret3="+ret3); 
       String ret4 = h3.handleFeeRequest("小张", 600);
       System.out.println("the ret4="+ret4); 
      
       String ret5 = h3.handleFeeRequest("小小", 1200); 
       System.out.println("the ret5="+ret5); 
       String ret6 = h3.handleFeeRequest("小张", 1200);
       System.out.println("the ret6="+ret6); 
    }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 220,063评论 6 510
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,805评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 166,403评论 0 357
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,110评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,130评论 6 395
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,877评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,533评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,429评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,947评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,078评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,204评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,894评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,546评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,086评论 0 23
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,195评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,519评论 3 375
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,198评论 2 357

推荐阅读更多精彩内容