【设计模式】一文速通责任链模式

86 阅读5分钟

1. 引入

设想一个这样的场景,公司有一个申请报销费用的流程,每个员工都可以提交费用报销申请,要求不同级别的费用需要不同级别的领导进行审批。

假定规则如下:

  • 报销费用小于等于1000元:员工可以自行提交报销;
  • 报销费用大于1000元:需要经过主管的批准;
  • 报销费用大于5000元:需要经过部门经理的批准;
  • 报销费用大于10000元:需要经过总经理的批准

实现上述需求,我们有以下两种方案。

1.1 方案一:if-else实现

1.1.1 实现代码

  • 定义报销金额类Expense
class Expense {  
    // 报销金额
    private double amount;  
    public Expense(double amount) {  
        this.amount = amount;  
    }  
    public double getAmount() {  
        return amount;  
    }  
}
  • 定义审批流程方法approveExpense()
public void approveExpense(Expense expense) {
    if (expense.getAmount() <= 1000) {
        System.out.println("员工自行提交报销,报销金额为:" + expense.getAmount());
        // 员工自行提交报销流程...
    } else if (expense.getAmount() <= 5000) {
        System.out.println("主管审批报销申请,报销金额为" + expense.getAmount());
        // 主管审批流程...
    } else if (expense.getAmount() <= 10000) {
        System.out.println("部门经理审批报销申请,报销金额为" + expense.getAmount());
        // 部门经理审批流程...
    } else {
        System.out.println("总经理审批报销申请,报销金额为" + expense.getAmount() + " exceeds approval limit.");
        // 总经理审批流程...
    }
}

1.1.2 上述方案存在问题

  • 代码臃肿approveExpense()方法中写了多个if-else判断条件,如果增加多条审批规则,那么 if-else判断就越多,代码的可读性较差

  • 违背了SOLID设计原则

    • 违背了开闭原则(对扩展开放,对修改关闭),每次修改审批条件或者审批层级都需要修改这个方法;
    • 违背了单一职责原则approveExpense()方法中承担了判断与决策的多个责任
  • 耦合度高:审批条件和审批过程耦合

1.2 方案二:采用责任链模式实现

1.2.1 实现代码

  1. 定义费用报销请求实体类Expense
// 费用报销请求类
class Expense {
    private double amount;

    public Expense(double amount) {
        this.amount = amount;
    }

    public double getAmount() {
        return amount;
    }
}
  1. 定义处理者接口ExpenseHandler
interface ExpenseHandler {
    void approve(Expense expense);
    void setNextHandler(ExpenseHandler nextHandler);
}
  1. 实现具体的处理者类
// 主管处理类
class DirectSupervisor implements ExpenseHandler {
    private static final double APPROVAL_LIMIT = 1000;
    private ExpenseHandler nextHandler;

    @Override
    public void approve(Expense expense) {
        if (expense.getAmount() <= APPROVAL_LIMIT) {
            System.out.println("主管审批报销申请,报销金额为" + expense.getAmount());
            // 主管审批流程...
        } else if (nextHandler != null) {
            nextHandler.approve(expense);
        }
    }

    @Override
    public void setNextHandler(ExpenseHandler nextHandler) {
        this.nextHandler = nextHandler;
    }
}


// 部门经理处理类
class DepartmentManager implements ExpenseHandler {
    private static final double APPROVAL_LIMIT = 5000;
    private ExpenseHandler nextHandler;

    @Override
    public void approve(Expense expense) {
        if (expense.getAmount() <= APPROVAL_LIMIT) {
            System.out.println("部门经理审批报销申请,报销金额为" + expense.getAmount());
            // 部门经理审批流程...
        } else if (nextHandler != null) {
            nextHandler.approve(expense);
        }
    }

    @Override
    public void setNextHandler(ExpenseHandler nextHandler) {
        this.nextHandler = nextHandler;
    }
}
// 总经理处理类
class GeneralManager implements ExpenseHandler {
    private static final double APPROVAL_LIMIT = 10000;

    @Override
    public void approve(Expense expense) {
        if (expense.getAmount() <= APPROVAL_LIMIT) {
             System.out.println("总经理审批报销申请,报销金额为" + expense.getAmount() + " exceeds approval limit.");
            // 总经理审批流程...
        } else {
            System.out.println("报销金额为" + expense.getAmount() + " 超出了总经理的权限");
        }
    }

    @Override
    public void setNextHandler(ExpenseHandler nextHandler) {
        throw new UnsupportedOperationException("总经理拥有最高权限,没有下一级处理者");
    }
}

image.png

1.2.2 上述方案优点

上述采用责任链设计模式的方案带来的好处

  • 请求者处理者二者解耦,避免了请求者需要知道处理者的具体情况;
  • 可以动态组合处理者处理请求,提高了系统的灵活性可扩展性

2. 责任链模式

2.1 定义

责任链模式,也称职责链模式Chain of Responsibility):是一种行为设计模式,允许将请求沿着处理者链进行发送,收到请求后,每个处理者均可对请求进行处理,或者将其传递给链上的下个处理者。

image.png

2.2 角色及结构

2.2.1 责任链模式的三种角色

  • Handler抽象的处理者,主要实现以下职责:

    • 定义一个请求的处理方法handle(),唯一对外开放的方法;
    • 定义一个链的编排方法setNext(),设置下一个处理者
  • ConcreteHandler具体的处理者,实现处理请求的实际代码

  • Client:客户端,创建处理链,向链头的具体处理者对象提交请求

2.2.2 责任链模式结构图

image.png

2.3 适用场景

  • 程序需要使用不同方式处理不同种类请求, 而且请求类型顺序预先未知

  • 必须按顺序执行多个处理者;

  • 所需处理者及其顺序必须在运行时进行改变

2.4 优缺点

2.4.1 优点

  • 可以控制请求处理的顺序

  • 遵守单一职责原则,可对发起操作和执行操作的类进行解耦;

  • 遵守开闭原则,可以在不更改现有代码的情况下在程序中新增处理者。

2.4.2 缺点

  • 性能问题:每个请求都是从链头遍历到链尾,链条比较长的情况下,性能是一个比较大的问题;

  • 调试不方便:链条较长会导致调试的逻辑比较复杂。

2.5 注意事项

链中节点数量需要控制,避免出现超长链的情况,一般的做法是在Handler中设置一个最大节点数量,在setNext方法中判断是否已经是超过其阈值超过则不允许该链建立,避免无意识地破坏系统性能。

参考资料