设计模式-责任链模式

一、定义

又叫职责链模式,它是一种对象行为模式,旨在避免发送请求者与多个处理请求者耦合在一起,它将所有请求的处理者通过前一对象记住其下一对象的引用而形成一条链;当有请求时,可将请求沿着这条链进行传递,直到有对象处理请求为止。

二、优点

1、降低了对象之间的耦合度,发送者无需知道具体由哪些对象处理其请求以及处理链的结构,发送者和接受者也无需拥有对方的明确信息。
2、增强了系统的可扩展性,可根据需求增加新的处理类,满足开闭原则
3、增强了给对象指派职责的灵活性。当处理流程发生变化时,可以动态地改变链条内的成员或调动他们的顺序,也可以动态地增加或者删除责任
4、责任链简化了对象之间的连接。每个对象只需保持一个指向其后继者的引用,无需保持其他所有处理者的引用,这避免了使用众多的 ( if ) 或者 ( if...else ) 语句。
5、处理者分工明确。每个处理者只需处理自己该处理的工作,不该处理的传递给下一个处理者完成;各个处理类明确自己的 责任范围,符合职责单一原则。

三、缺点

1、无法保证每个请求一定被处理。由于一个请求没有明确的接受者,因此无法保证该请求一定会被处理,即便一直传到链的末端也得不到处理。
2、对于比较长的职责链,会涉及到比较多的处理对象,系统性可能会受到一定影响。
3、职责链建立的合理性需要靠客户端来保证,增加了客户端的复杂性,可能会由于职责链的错误设置而导致系统出错,比如可能会造成循环调用。

四、模式结构与实现

1)模式的结构

抽象处理者(Handler):处理请求的接口,它定义了一个处理的抽象方法和一个后继连接。
具体处理者(Concrete Handler):继承或实现抽象处理者。具体实现抽象处理者的处理方法,判断能否处理本次请求,可以则处理,不可以则将请求转给它的后继者。
客户类:创建处理链接,并向链头的具体处理者提交请求,它并不关心处理的细节及请求在链上的传递过程。
结构图:

责任链模式结构图.png

客户端可按下图设置责任链:
责任链.png

2)模式的实现

//客户端请求类
public class ChainOfResponsibilityPattern {
    public static void main(String[] args) {
        //组装责任链 
        Handler handler1=new ConcreteHandler1(); 
        Handler handler2=new ConcreteHandler2(); 
        handler1.setNext(handler2); 
       //提交请求 
       handler1.handleRequest("two");
    }
}
//抽象处理者角色
abstract class Handler {
    private Handler next;
    public void setNext(Handler next) {
        this.next=next; 
    }
    public Handler getNext() { 
        return next; 
    }   
    //处理请求的方法
    public abstract void handleRequest(String request);       
}
//具体处理者角色1
class ConcreteHandler1 extends Handler {
    public void handleRequest(String request) {
        if(request.equals("one")) {
            System.out.println("具体处理者1负责处理该请求!");       
        } else {
            if(getNext() != null) {
                getNext().handleRequest(request);             
            } else {
                System.out.println("没有人处理该请求!");
            }
        } 
    } 
}
//具体处理者角色2
class ConcreteHandler2 extends Handler {
    public void handleRequest(String request) {
        if(request.equals("two")) {
            System.out.println("具体处理者2负责处理该请求!");       
        } else {
            if(getNext() != null) {
                getNext().handleRequest(request);             
            } else {
                System.out.println("没有人处理该请求!");
            }
        } 
    }
}

运行结果:

具体处理者2负责处理该请求!

五、应用场景

责任链模式通常在以下几种情况使用:
1、有多个对象可以处理一个请求,哪个对象处理该请求由运行时刻自动确定。
2、可动态指定一组对象处理请求,或添加新的处理者。
3、在不明确指定请求处理者的情况下,向多个处理者中的一个提交请求。

六、应用实例

1)需求

用责任链模式设计一个请假条审批模块。

2)分析

规定学生请假小于或等于 2 天,班主任可以批准;小于或等于 7 天,系主任可以批准;小于或等于 10 天,院长可以批准;其他情况不予批准;这个实例适合使用职责链模式实现。

3)设计思路

1、抽象处理类
定义一个领导类(Leader),它是抽象处理者,包含了一个指向下一位领导的指针 next 和一个处理假条的抽象处理方法 handleRequest(int LeaveDays);
2、具体处理类
定义班主任类(ClassAdviser)、系主任类(DepartmentHead)和院长类(Dean),它们是抽象处理者的子类,是具体处理者,必须根据自己的权力去实现父类的 handleRequest(int LeaveDays) 方法,如果无权处理就将假条交给下一位具体处理者。
3、客户类
负责创建处理链,并将假条交给链头的具体处理者(班主任)。
4、结构图

类图.png

4)程序实现

public class LeaveApprovalTest {
    public static void main(String[] args) {
        //组装责任链 
        Leader teacher1=new ClassAdviser();
        Leader teacher2=new DepartmentHead();
        Leader teacher3=new Dean();
        //Leader teacher4=new DeanOfStudies();
        teacher1.setNext(teacher2);
        teacher2.setNext(teacher3);
        //teacher3.setNext(teacher4);
        //提交请求 
        teacher1.handleRequest(8);
    }
}
//抽象处理者:领导类
abstract class Leader {
    private Leader next;
    public void setNext(Leader next) {
        this.next=next; 
    }
    public Leader getNext() { 
        return next; 
    }   
    //处理请求的方法
    public abstract void handleRequest(int LeaveDays);       
}
//具体处理者1:班主任类
class ClassAdviser extends Leader {
    public void handleRequest(int LeaveDays) {
        if(LeaveDays <= 2)  {
            System.out.println("班主任批准您请假" + LeaveDays + "天。");       
        } else {
            if(getNext() != null)  {
                getNext().handleRequest(LeaveDays);             
            } else {
                  System.out.println("请假天数太多,没有人批准该假条!");
            }
        } 
    } 
}
//具体处理者2:系主任类
class DepartmentHead extends Leader {
    public void handleRequest(int LeaveDays) {
        if(LeaveDays <= 7)  {
            System.out.println("系主任批准您请假" + LeaveDays + "天。");       
        } else {
            if(getNext() != null)  {
                  getNext().handleRequest(LeaveDays);             
            } else {
                System.out.println("请假天数太多,没有人批准该假条!");
           }
        } 
    } 
}
//具体处理者3:院长类
class Dean extends Leader {
    public void handleRequest(int LeaveDays) {
        if(LeaveDays <= 10)  {
            System.out.println("院长批准您请假" + LeaveDays + "天。");       
        } else {
            if(getNext() != null) {
                getNext().handleRequest(LeaveDays);             
            } else {
                  System.out.println("请假天数太多,没有人批准该假条!");
            }
        } 
    } 
}
//具体处理者4:教务处长类
class DeanOfStudies extends Leader {
    public void handleRequest(int LeaveDays) {
        if(LeaveDays <= 20)  {
            System.out.println("教务处长批准您请假"+LeaveDays+"天。");       
        } else {
            if(getNext()!=null) {
                getNext().handleRequest(LeaveDays);          
            } else {
                  System.out.println("请假天数太多,没有人批准该假条!");
            }
        } 
    } 
}

程序运行结果如下:

院长批准您请假8天。

5)扩展

假如增加一个教务处长类,可以批准学生请假 20 天,也非常简单,代码如下:

//具体处理者4:教务处长类
class DeanOfStudies extends Leader {
    public void handleRequest(int LeaveDays) {
        if(LeaveDays<=20) {
            System.out.println("教务处长批准您请假"+LeaveDays+"天。");
        } else {
            if(getNext()!=null) {
                getNext().handleRequest(LeaveDays);
            } else {
                System.out.println("请假天数太多,没有人批准该假条!");
            }
        }
    }
}

七、责任链模式在开源项目中的应用

1)Spring MVC
在springMVC中,DispatcherServlet这个核心类中使用到了HandlerExecutionChain这个类,他就是责任链模式实行的具体类。
2)Servlet过滤器
request,response参数通过两个过滤器 HTMLFilter SesitiveFilter 过滤掉一些非法内容。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,921评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,635评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,393评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,836评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,833评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,685评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,043评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,694评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,671评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,670评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,779评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,424评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,027评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,984评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,214评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,108评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,517评论 2 343