采购单的分级审批
每个公司可能都存在自己的采购审批模式,那么如何在软件中设计这样一个采购单的分级审批?
假如 Sunny 软件公司开发人员提出了一个初始解决方法,在系统中 提供一个采购单处理类 PurchaseRequestHandler
用于才统一处理采购单,代码如下:
// 采购单处理类
class PurchaseRequestHandler {
// 递交采购单给主任
public void sendRequestToDirector(PurchaseRequest request) {
if (request.getAmount() < 50000) {
// 主任可以审批该采购单
this.handleByDirector(request);
}
else if (request.getAmount() < 100000) {
// 副董事长可审批
this.handleByVicePresident(request);
}
else if (request.getAmount() < 500000) {
// 董事长可审批
this.handleByPresident(request);
}
else {
// 董事会可审批
this.handleByCongress(request);
}
}
// 主任审批采购单
public void handleByDirector(PurchaseRequest request) {
// 代码省略
}
public void handleByVicePresident(PurchaseRequest request) {
// to do
}
public void handleByPresident(PurchaseRequest request) {
// to do
}
public void handleByCongress(PurchaseRequest request) {
// to do
}
通过上面的代码很容易就能实现流程审批,但仔细发现, 会有 3 个问题:
-
PurchaseRequestHandle
类较为庞大,各个级别的审批方法都集中在一个类中,违反了单一职责原则,测试和维护难度大。 - 如果需要新增一个新的审批级别,或者调整任何一级的审批金额和审批细节时,都需要修改源代码并进行严格的测试;如果需要移除某一级别(例如金额为 10 万元及以上的采购单直接由董事长审批,不再设副董事长一职)时也必须对源代码进行修改,违反了开闭原则。
- 审批流程的设置缺乏灵活性,现在的审批流程是“主任->副董事长->董事长->董事会”,如果需要改为“主任->董事长->董事会”,在此方案中只能通过修改源代码来实现,客户端无法定制审批流程。
针对这 3 个缺点,可以通过责任链模式解决。
责任链模式
责任链模式,又被称为职责链模式、命令链、CoR、Chain of Command、Chain of Responsibility。
责任链模式是一种行为设计模式, 允许你将请求沿着处理者链进行发送。 收到请求后, 每个处理者均可对请求进行处理, 或将其传递给链上的下个处理者。
责任链模式为请求创建了一个处理对象的链。避免将请求发送者与接收者耦合在一起,让多个对象都有机会接收请求,连接成一条链,并且沿着这条链传递请求,直到有对象处理它为止。
发起请求和具体处理请求的过程进行解耦:责任链上的处理者负责处理请求,客户只需要将请求发送到责任链上即可,无须关心请求的处理细节和请求的传递。
责任链模式结构
- 抽象处理者 (Handler) 声明了所有具体处理者的通用接口。一般设计为抽象类, 该接口通常仅包含单个方法用于请求处理, 但有时其还会包含一个设置链上下个处理者的方法。
- 基础处理者 (Base Handler) 是一个可选的类, 你可以将所有处理者共用的样本代码放置在其中。
通常情况下, 该类中定义了一个保存对于下个处理者引用的成员变量。 客户端可通过将处理者传递给上个处理者的构造函数或设定方法来创建链。 该类还可以实现默认的处理行为: 确定下个处理者存在后再将请求传递给它。
- 具体处理者 (Concrete Handlers) 包含处理请求的实际代码。 每个处理者接收到请求后, 都必须决定是否进行处理, 以及是否沿着链传递请求。
处理者通常是独立且不可变的, 需要通过构造函数一次性地获得所有必要地数据。
- 客户端 (Client) 可根据程序逻辑一次性或者动态地生成链。 值得注意的是, 请求可发送给链上的任意一个处理者, 而非必须是第一个处理者。
抽象处理者的典型代码如下:
abstract class Handler {
protected Handler successor;
public void setSuccessor(Handler successor) {
this.successor = successor;
}
public abstract void handleRequest(String request);
}
上述代码中,抽象处理者类定义了对下家的引用对象,以便将请求转发给下家,该对象的访问符可设为 protected,在其子类中可以使用。在抽象处理类中声明抽象的请求处理方法,具体实现交给子类完成。
具体处理者的两个作用:
- 处理请求,不同的具体处理者以不同的形式实现抽象请求处理方法
handleRequest()
- 转发请求,如果该请求超出了当前处理者类的处理范围,可以将该请求转发给下家
代码如下:
class ConcreteHandler extends Handler {
public void handleRequest(String request) {
if (请求满足条件) {
// 处理请求
}
else {
this.successor.handleRequest(request); // 转发请求
}
}
}
责任链模式适用场景
- 当程序需要使用不同方式处理不同种类请求, 而且请求类型和顺序预先未知时, 可以使用责任链模式。该模式能将多个处理者连接成一条链。 接收到请求后, 它会 “询问” 每个处理者是否能够对其进行处理。 这样所有处理者都有机会来处理请求。
- 当必须按顺序执行多个处理者时, 可以使用该模式。无论你以何种顺序将处理者连接成一条链, 所有请求都会严格按照顺序通过链上的处理者。
- 如果所需处理者及其顺序必须在运行时进行改变, 可以使用责任链模式。如果在处理者类中有对引用成员变量的设定方法, 你将能动态地插入和移除处理者, 或者改变其顺序。