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 实现代码
- 定义费用报销请求实体类
Expense
// 费用报销请求类
class Expense {
private double amount;
public Expense(double amount) {
this.amount = amount;
}
public double getAmount() {
return amount;
}
}
- 定义处理者接口
ExpenseHandler
interface ExpenseHandler {
void approve(Expense expense);
void setNextHandler(ExpenseHandler nextHandler);
}
- 实现具体的处理者类
// 主管处理类
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("总经理拥有最高权限,没有下一级处理者");
}
}
1.2.2 上述方案优点
上述采用责任链设计模式的方案带来的好处:
- 将请求者和处理者二者解耦,避免了请求者需要知道处理者的具体情况;
- 可以动态组合处理者处理请求,提高了系统的灵活性和可扩展性。
2. 责任链模式
2.1 定义
责任链模式,也称职责链模式(Chain of Responsibility
):是一种行为设计模式,允许将请求沿着处理者链进行发送,收到请求后,每个处理者均可对请求进行处理,或者将其传递给链上的下个处理者。
2.2 角色及结构
2.2.1 责任链模式的三种角色
-
Handler
:抽象的处理者,主要实现以下职责:- 定义一个请求的处理方法
handle()
,唯一对外开放的方法; - 定义一个链的编排方法
setNext()
,设置下一个处理者
- 定义一个请求的处理方法
-
ConcreteHandler
:具体的处理者,实现处理请求的实际代码 -
Client
:客户端,创建处理链,向链头的具体处理者对象提交请求
2.2.2 责任链模式结构图
2.3 适用场景
-
程序需要使用不同方式处理不同种类请求, 而且请求类型和顺序预先未知;
-
必须按顺序执行多个处理者;
-
所需处理者及其顺序必须在运行时进行改变。
2.4 优缺点
2.4.1 优点
-
可以控制请求处理的顺序;
-
遵守单一职责原则,可对发起操作和执行操作的类进行解耦;
-
遵守开闭原则,可以在不更改现有代码的情况下在程序中新增处理者。
2.4.2 缺点
-
性能问题:每个请求都是从链头遍历到链尾,链条比较长的情况下,性能是一个比较大的问题;
-
调试不方便:链条较长会导致调试的逻辑比较复杂。
2.5 注意事项
链中节点数量需要控制,避免出现超长链的情况,一般的做法是在
Handler
中设置一个最大节点数量,在setNext
方法中判断是否已经是超过其阈值,超过则不允许该链建立,避免无意识地破坏系统性能。
参考资料
- refactoringguru.cn/design-patt…
- 《设计模式之禅》