一、场景问题
某公司决定设计一套线上的费用报销的审核机制,其中
- 金额在1000以内只需要由项目经理审批即可
- 金额在1000~5000则需要由部门经理审批
- 金额大于5000则必须由总经理审批
请设计一套程序实现上述逻辑
二、传统解决方案
1、解决思路
根据不同的金额走不同的审批分支
-
代码展示
-
报销单实体
public class FeeForm { /** * 报销单号 */ private Integer id; /** * 报销金额 */ private double amount; /** * 报销人姓名 */ private String name; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public double getAmount() { return amount; } public void setAmount(double amount) { this.amount = amount; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
-
报销单时审批类
public class FeeFormCheck { /** * 报销单审核 * * @param feeForm 报销单 * @return void **/ public static void checkFeeForm(FeeForm feeForm) { double amount = feeForm.getAmount(); if (amount < 1000) { System.out.println( "项目经理同意" + feeForm.getName() + "的" + feeForm.getAmount() + "元的报销申请"); } else if (amount >= 1000 && amount <= 5000) { System.out.println( "部门经理同意" + feeForm.getName() + "的" + feeForm.getAmount() + "元的报销申请"); } else { System.out.println( "总经理同意" + feeForm.getName() + "的" + feeForm.getAmount() + "元的报销申请"); } } }
-
-
客户端测试
public static void main(String[] args) { FeeForm feeForm1 = new FeeForm(); feeForm1.setAmount(499); feeForm1.setName("张三"); FeeForm feeForm2 = new FeeForm(); feeForm2.setAmount(3982); feeForm2.setName("张三"); FeeForm feeForm3 = new FeeForm(); feeForm3.setAmount(20000); feeForm3.setName("张三"); FeeFormCheck.checkFeeForm(feeForm1); FeeFormCheck.checkFeeForm(feeForm2); FeeFormCheck.checkFeeForm(feeForm3); }
输出
项目经理同意张三的499.0元的报销申请 部门经理同意张三的3982.0元的报销申请 总经理同意张三的20000.0元的报销申请
2、弊端
- 违背开闭原则,扩展性差,审核逻辑复杂时会导致代码臃肿。
- 如果处理流程是项目经理->部门经理->总经理,上述写法会导致处理逻辑缺乏灵活性。
- 处理逻辑糅和在一起,没有做到职责分离与隐藏的功能
三、模式剖析
1、模式定义
责任链模式(Chain of Responsibility Pattern):将所有请求的处理者通过前一对象记住其下一个对象的引用而连成一条链;当有请求发生时,可将请求沿着这条链传递,直到有对象处理它为止。
2、模式结构
职责链模式一般包含如下角色
-
Handler
:抽象处理者类定义一个处理请求的接口,包含抽象处理方法和一个后继连接。
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); }
-
Concrete Handler
:具体处理者类实现抽象处理者的处理方法,判断能否处理本次请求,如果可以处理请求则处理,否则将该请求转给它的后继者。
//具体处理者角色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("没有人处理该请求!"); } } } }
-
Receiver
:实现者/接收者角色执行命令功能的相关操作,是具体命令对象业务的真正实现者
class Receiver { public void action() { System.out.println("接收者的action()方法被调用..."); } }
上面案例是持有策略的集合,这里是持有单个策略的引用。
-
Client
:客户端角色创建处理链,并向链头的具体处理者对象提交请求,它不关心处理细节和请求的传递过程
3、模式结构图
4、使用职责链模式改进案例
4.1、代码展示
-
报销单实体同上
-
抽象审批类
public abstract class Approver { /** * 后继审批者 */ private Approver next; public Approver getNext() { return next; } public void setNext(Approver next) { this.next = next; } /** * 审批 * @param feeForm 1 * @return void **/ public abstract void approve(FeeForm feeForm); }
-
具体审批类
-
项目经理
public class ProjectManagerApprover extends Approver { public ProjectManagerApprover() { } public ProjectManagerApprover(Approver next) { setNext(next); } @Override public void approve(FeeForm feeForm) { if (feeForm.getAmount() < 1000) { System.out.println( "项目经理同意" + feeForm.getName() + "的" + feeForm.getAmount() + "元的报销申请"); } else { Approver next = getNext(); if (next != null) { next.approve(feeForm); } else { System.out.println("金额过大,无人审批!"); } } } }
-
部门经理
public class DepartmentManagerApprover extends Approver { public DepartmentManagerApprover() { } public DepartmentManagerApprover(Approver next) { setNext(next); } @Override public void approve(FeeForm feeForm) { if (feeForm.getAmount() >= 1000 && feeForm.getAmount() <= 5000) { System.out.println( "部门经理同意" + feeForm.getName() + "的" + feeForm.getAmount() + "元的报销申请"); } else { Approver next = getNext(); if (next != null) { next.approve(feeForm); } else { System.out.println("金额过大,无人审批!"); } } } }
-
总经理
public class GeneralManagerApprover extends Approver { public GeneralManagerApprover() { } public GeneralManagerApprover(Approver next) { setNext(next); } @Override public void approve(FeeForm feeForm) { if (feeForm.getAmount() > 5000) { System.out.println( "总经理同意" + feeForm.getName() + "的" + feeForm.getAmount() + "元的报销申请"); } else { Approver next = getNext(); if (next != null) { next.approve(feeForm); } else { System.out.println("金额过大,无人审批!"); } } } }
-
4.2、客户端测试
public static void main(String[] args) {
FeeForm feeForm1 = new FeeForm();
feeForm1.setAmount(499);
feeForm1.setName("张三");
FeeForm feeForm2 = new FeeForm();
feeForm2.setAmount(3982);
feeForm2.setName("张三");
FeeForm feeForm3 = new FeeForm();
feeForm3.setAmount(20000);
feeForm3.setName("张三");
//创建审批的职责链
Approver approver = new ProjectManagerApprover(new DepartmentManagerApprover(new GeneralManagerApprover()));
approver.approve(feeForm1);
approver.approve(feeForm2);
approver.approve(feeForm3);
}
输出
项目经理同意张三的499.0元的报销申请
部门经理同意张三的3982.0元的报销申请
总经理同意张三的20000.0元的报销申请
5、优缺点
- 优点
- 职责链模式使得一个对象无须知道是其他哪一个对象处理其请求,对象仅需知道该请求会被处理即可,接收者和发送者都没有对方的明确信息,且链中的对象不需要知道链的结构,由客户端负责链的创建,降低了系统的耦合度。
- 请求处理对象仅需维持一个指向其后继者的引用,而不需要维持它对所有的候选处理者的引用,可简化对象的相互连接。
- 在给对象分派职责时,职责链可以给我们更多的灵活性,可以通过在运行时对该链进行动态的增加或修改来增加或改变处理一个请求的职责。
- 在系统中增加一个新的具体请求处理者时无须修改原有系统的代码,只需要在客户端重新建链即可,从这一点来看是符合“开闭原则”的
- 缺点
- 由于一个请求没有明确的接收者,那么就不能保证它一定会被处理,该请求可能一直到链的末端都得不到处理;一个请求也可能因职责链没有被正确配置而得不到处理。
- 对于比较长的职责链,请求的处理可能涉及到多个处理对象,系统性能将受到一定影响,而且在进行代码调试时不太方便。
- 如果建链不当,可能会造成循环调用,将导致系统陷入死循环
6、使用场景
- 有多个对象可以处理同一个请求,具体哪个对象处理该请求待运行时刻再确定,客户端只需将请求提交到链上,而无须关心请求的处理对象是谁以及它是如何处理的
- 在不明确指定接收者的情况下,向多个对象中的一个提交一个请求。
- 可动态指定一组对象处理请求,客户端可以动态创建职责链来处理请求,还可以改变链中处理者之间的先后次序