《Java Design Patterns》第十四章 责任链模式

责任链模式

第十四章 责任链模式

一、设计一款请假程序

Sunny 软件公司的 OA 系统需要提供一个假条审批模块:

如果员工请假天数小于3天,主任可以审批;大于等于3天,小于10天,经理可以审批;大于等于10天,总经理审批;超过30天,拒绝。

1.1 初步设计

在没学责任链模式之前的设计可能如下:

class ChainOfResponsibilityPattern {
    public void handleRequset(Integer days) {
        if(days < 3){
            this.director(days);
        }
        else if(days < 10){
            this.manager(days);
        }
        else if(days < 30){
            this.gManager(days);
        }
        else {
            throw new RuntimeException("error handler");
        }
    }

    public void director(Integer days){
        // 主任处理。。。
    }

    // .......
}

1.2 存在的问题

  1. ChainOfResponsibilityPattern 类十分的庞大,所有的审批方法都集中在一个类中,违反了“单一职责原则”,测试和维护难度大
  2. 如果需要增加一个新的审批登记,或者调整任意一级的审批权限,都需要修改源代码。违反了“开闭原则”
  3. 审批流程的设置缺乏灵活性,当设计完 主任》经理》总经理》异常 的流程后,再想修改,必须直接修改源代码,客户端无法定制流程

1.3 解决方案

使用责任链模式来重构。

二、责任链模式

2.1 定义

责任链模式 - Chain Of Responsibility Pattern:避免请求发送者和接收者耦合在一起,让多个对象都有可能接受请求,将这些请求连接成一条链,并且沿着这条链传递请求,直到有对象处理它为止。

责任链模式是一种对象行为型模式。

责任链模式

2.2 责任链模式中的几个角色

  1. Handler - 抽象处理者
    1. 定义了处理请求的抽象接口
    2. 定义一个抽象处理者类型的对象引用,作为对下家的引用
    3. 通过该引用,处理者可以形成一条链式调用
  2. ConcreteHandler - 具体处理者
    1. 具体处理者有两大作用
    2. 第一,处理请求,不同的处理者以不同形式实现抽象处理方法
    3. 第二,转发请求,如果请求不再当前处理者的权限内,可以将请求转发给下家

2.3 责任链模式的核心 - 抽象处理者类

abstract class Handler {
    protected Handler successor;

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

    public abstract void handleRequest(String request);
}

2.4 具体处理者

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

三、完整实现

/**
 * 职责链模式
 * Sunny 软件公司的 OA 系统需要提供一个假条审批模块:
 * 如果员工请假天数小于3天,主任可以审批
 * 大于等于3天,小于10天,经理可以审批
 * 大于等于10天,总经理审批;超过30天,拒绝
 */
// 抽象处理者 - 经理、主任等具体处理者的抽象父类
abstract class Handler {
    protected Handler successor;

    // 指向下一个具体实现类的引用,通过该引用,处理者可以连成一条线
    public void setSuccessor(Handler successor) {
        this.successor = successor;
    }

    // 抽象处理方法,交给具体实现类实现
    protected abstract void handleRequest(Integer days);
}

// 主任 - 具体处理者
class DirectorHandler extends Handler {

    // 如果在处理范围内,则进行处理,否则继续向下转发
    @Override
    protected void handleRequest(Integer days) {
        if (days < 3) {
            System.out.println("角色[主任],处理了员工请假请求,请假天数为[" + days + "]天");
        } else {
            super.successor.handleRequest(days);
        }
    }
}

// 经理 - 具体处理者
class ManagerHandler extends Handler {
    @Override
    protected void handleRequest(Integer days) {
        if (days < 10)
            System.out.println("角色[经理],处理了员工请假请求,请假天数为[" + days + "]天");
        else
            super.successor.handleRequest(days);
    }
}

// 总经理 - 具体处理者
class GManagerHandler extends Handler {
    @Override
    protected void handleRequest(Integer days) {
        if (days < 30)
            System.out.println("角色[总经理],处理了员工请假请求,请假天数为[" + days + "]天");
        else
            super.successor.handleRequest(days);
    }
}

// 错误 - 具体处理者
class ErrorHandler extends Handler {
    @Override
    protected void handleRequest(Integer days) {
        System.out.println("请假失败,当前请假天数[" + days + "]超过最大限制!");
    }
}

public class ChainOfResponsibilityPattern {

    public static void main(String[] args) {
        Handler director = new DirectorHandler();
        Handler manager = new ManagerHandler();
        Handler gManager = new GManagerHandler();
        Handler error = new ErrorHandler();

        director.setSuccessor(manager);
        manager.setSuccessor(gManager);
        gManager.setSuccessor(error);

        director.handleRequest(3);
        director.handleRequest(11);
        director.handleRequest(44);
    }
}

四、纯与不纯的责任链模式

4.1 纯的责任链模式

纯责任链模式,要求一个具体处理者对象只能在两个行为中选择一个,要么承担全部责任,要么将责任推给下家。

4.2 不纯的责任链模式

不纯的责任链模式,允许某个请求被一个具体处理者部分处理后向下传递,或者该请求能够被多个处理器处理。

五、总结

优点

  1. 责任链模式使一个对象无须知道是哪个对象处理其请求,只需要知道该请求会被处理即可。接收者与发送者互不相知,且链中对象不需要知道链的结构,由客户端负责链的创建,降低了系统的耦合度
  2. 请求处理对象仅需维持一个向后继者的引用,不需要维持所有后继者的引用,简化对象的相互连接
  3. 在给对象分派职责时,责任链可以给我们更多的灵活性,可通过在运行时对该链动态增加或修改一个请求的职责
  4. 新增请求无需修改原有代码,只需重新建链即可,符合“开闭原则”

缺点

  1. 不能保证请求必定会被执行,该请求可能到链的末尾也没有匹配到合适的处理者;或者责任链的配置有误,也可能导致请求未被处理
  2. 如果责任链较长,请求的处理会涉及多个对象,系统性能会受到一定影响,且在调试时不太方便
  3. 如果链表建立有误,可能会陷入循环调用

适用场景

  1. 有多个对象可以处理同一个请求,具体哪个对象处理该请求待运行时刻再确定,客户端只需将请求提交到连上,无须关心请求的处理对象是谁,以及如何处理的
  2. 在不明确指定接收者的情况下,向多个对象中的一个,提交一个请求
  3. 动态指定一组对象处理请求,可以动态创建责任链,动态改变链中处理者的先后次序等
文章作者: koral
文章链接: http://luokaiii.github.io/2019/07/15/读书笔记/《JavaDesignPatterns》/16.责任链模式/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自