设计模式系列篇(十五)——职责链模式

What

职责链模式(Chain Of Responsibility Design Pattern)是一种行为设计模型。该模式将请求的发送和接收解耦,让多个接收对象都有机会处理这个请求。将这些接收对象串成一条链,并沿着这条链传递这个请求,直到链上的某个接收对象能够处理它为止。通过定义可以看出,请求只会被职责链上的某一节点处理,而且该请求一旦被处理,则不会继续往下传递。但在实际生产中,职责链模式则会十分灵活,允许请求经过职责链上的所有节点的处理,如敏感词过滤器的设计,我们会使用多种不同的敏感词过滤算法组成职责链,分别对输入文本进行敏感词的过滤。

Why

  1. 对象仅需知道该请求会被处理即可,且链中的对象不需要知道链的结构,由客户端负责链的创建,降低了系统的耦合度。
  2. 请求处理对象仅需维持一个指向其后继者的引用,而不需要维持它对所有的候选处理者的引用,可简化对象的相互连接。
  3. 在给对象分派职责时,职责链可以给我们更多的灵活性,可以在运行时对该链进行动态的增删改,改变处理一个请求的职责。
  4. 新增一个新的具体请求处理者时无须修改原有代码,只需要在客户端重新建链即可,符合 "开闭原则"。

When

一个事件需要经过多个对象处理是一个挺常见的场景,譬如采购审批流程,请假流程,软件开发中的异常处理流程,web请求处理流程等各种各样的流程,可以考虑使用责任链模式来实现。

How

职责链模式包含以下几个类:
Handler(抽象处理者):它定义了一个处理请求的接口,一般设计为抽象类,由于不同的具体处理者处理请求的方式不同,因此在其中定义了抽象请求处理方法。因为每一个处理者的下家还是一个处理者,因此在抽象处理者中定义了一个抽象处理者类型的对象,作为其对下家的引用。通过该引用,处理者可以连成一条链。
ConcreteHandler(具体处理者):它是抽象处理者的子类,可以处理用户请求,在具体处理者类中实现了抽象处理者中定义的抽象请求处理方法,在处理请求之前需要进行判断,看是否有相应的处理权限,如果可以处理请求就处理它,否则将请求转发给后继者;在具体处理者中可以访问链中下一个对象,以便请求的转发。
HandlerChain(处理客户端):该类定义了对象链的初始化、增加及删除,以及职责链的调用入口。对象链有两种实现方式:一种是使用链表来存储处理器,另一种是使用数组来存储处理器,后面一种实现方式更加简单。
下面,我们就以奖学金的申请流程为例,来向大家介绍职责链模式的实现方式。奖学金申请时需要经过填写奖学金申请表、导师签字、学院审核、学校审核这几个大的步骤,每一步都可以抽象为一个ConcreteHandler类,继承自一个抽象类Handler。
首先,我们先定义奖学金申请表格——ScholarshipApplyForm类;

public class ScholarshipApplyForm {
    private String name;
    private String ID;
    private String studentId;
    private String applyCause;
    private String mentor;
    private String school;
    private String mentorSuggestion;
    private String schoolSuggestion;
    private String universitySuggestion;
    private String status;

    // Omitting Getter And Setter
}

接下来,抽象类Handler及其子类。

public abstract class Handler {
    protected Handler successor = null;

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

    public final void handle() {
        doHandle();
        if (successor != null) {
            successor.handle();
        }
    }

    protected abstract void doHandle();
}

这里使用模版模式,将调用 successor.handle() 的逻辑从具体的处理器类中剥离出来,放到抽象父类中。这样具体的处理器类只需要实现自己的业务逻辑就可以了。

接下来,我们分别实现其子类。

public class StudentHandler extends Handler {

    @Override
    protected void doHandle(ScholarshipApplyForm form) {
        form.setStatus("The form has been fulfilled. Wait for mentor to sign");
    }
}

public class MentorHandler extends Handler {

    @Override
    protected void doHandle(ScholarshipApplyForm form) {
        form.setStatus("The mentor has signed. Wait for school to approve.");
    }
}

public class SchoolHandler extends Handler {

    @Override
    protected void doHandle(ScholarshipApplyForm form) {
        form.setStatus("The school has approved. Wait for university to approve.");
    }
}

public class UniversityHandler extends Handler {

    @Override
    protected void doHandle(ScholarshipApplyForm form) {
        form.setStatus("The university has approved. Award scholarships.");
    }
}

然后,我们定义HandlerChain类,这里我们采用链表的方式实现。

public class HandlerChain {
    private Handler head = null;
    private Handler tail = null;

    public void addHandler(Handler handler) {
        handler.setSuccessor(null);
        if (head == null) {
            head = handler;
            tail = handler;
            return;
        }
        tail.setSuccessor(handler);
        tail = handler;
    }

    public void handle(ScholarshipApplyForm form) {
        if (head != null) {
            head.handle(form);
        }
    }
}

最后,测试类走起。

public class TestMain {
    public static void main(String[] args) {
        ScholarshipApplyForm form = new ScholarshipApplyForm();
        form.setName("Jeremy");
        form.setID("302911202009010011");
        form.setStudentId("20200901111");
        form.setApplyCause("I am so great!");
        form.setMentor("Tom");
        form.setSchool("Software School");

        HandlerChain chain = new HandlerChain();
        Handler studentHandler = new StudentHandler();
        Handler mentorHandler = new MentorHandler();
        Handler schoolHandler = new SchoolHandler();
        Handler universityHandler = new UniversityHandler();
        chain.addHandler(studentHandler);
        chain.addHandler(mentorHandler);
        chain.addHandler(schoolHandler);
        chain.addHandler(universityHandler);

        chain.handle(form);
    }
}

输出如下:

The form has been fulfilled. Wait for mentor to sign.
The mentor has signed. Wait for school to approve.
The school has approved. Wait for university to approve.
The university has approved. Award scholarships.

代码地址

i-learning

写在最后

如果你觉得我写的文章帮到了你,欢迎点赞、评论、分享、赞赏哦,你们的鼓励是我不断创作的动力~

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