目录
引言
一、概述
在现实生活中,一个事件需要经过多个对象处理是很常见的场景。例如,采购审批流程、请假流程等。公司员工请假,可批假的领导有部门负责人、副总经理、总经理等,但每个领导能批准的天数不同,员工必须根据需要请假的天数去找不同的领导签名,也就是说员工必须记住每个领导的姓名、电话和地址等信息,这无疑增加了难度。
在计算机软硬件中也有相关例子,如总线网中数据报传送,每台计算机根据目标地址是否同自己的地址相同来决定是否接收;还有异常处理中,处理程序根据异常的类型决定自己是否处理该异常;还有 Struts2的拦截器、JSP和 Servlet 的 Filter 等,所有这些,都可以考虑使用责任链模式来实现。
定义与特点
责任链(Chain of Responsibility)模式的定义:为了避免请求发送者与多个请求处理者耦合在一起,于是将所有请求的处理者通过前一对象记住其下一个对象的引用而连成一条链;当有请求发生时,可将请求沿着这条链传递,直到有对象处理它为止。
在责任链模式中,客户只需要将请求发送到责任链上即可,无须关心请求的处理细节和请求的传递过程,请求会自动进行传递。所以责任链将请求的发送者和请求的处理者解耦了。
优点如下:
降低了对象之间的耦合度。该模式使得一个对象无须知道到底是哪一个对象处理其请求以及链的结构,发送者和接收者也无须拥有对方的明确信息。
增强了系统的可扩展性。可以根据需要增加新的请求处理类,满足开闭原则。
增强了给对象指派职责的灵活性。当工作流程发生变化,可以动态地改变链内的成员或者调动它们的次序,也可动态地新增或者删除责任。
责任链简化了对象之间的连接。每个对象只需保持一个指向其后继者的引用,不需保持其他所有处理者的引用,这避免了使用众多的 if 或者 if···else 语句。
责任分担。每个类只需要处理自己该处理的工作,不该处理的传递给下一个对象完成,明确各类的责任范围,符合类的单一职责原则。
缺点如下:
不能保证每个请求一定被处理。由于一个请求没有明确的接收者,所以不能保证它一定会被处理,该请求可能一直传到链的末端都得不到处理。
对比较长的职责链,请求的处理可能涉及多个处理对象,系统性能将受到一定影响。
职责链建立的合理性要靠客户端来保证,增加了客户端的复杂性,可能会由于职责链的错误设置而导致系统出错,如可能会造成循环调用。
模式的结构与实现:
模式的结构
职责链模式主要包含以下角色。
抽象处理者(Handler)角色:定义一个处理请求的接口,包含抽象处理方法和一个后继连接。
具体处理者(Concrete Handler)角色:实现抽象处理者的处理方法,判断能否处理本次请求,如果可以处理请求则处理,否则将该请求转给它的后继者。
客户类(Client)角色:创建处理链,并向链头的具体处理者对象提交请求,它不关心处理细节和请求的传递过程。
责任链模式的本质是解耦请求与处理,让请求在处理链中能进行传递与被处理;理解责任链模式应当理解其模式,而不是其具体实现。责任链模式的独到之处是将其节点处理者组合成了链式结构,并允许节点自身决定是否进行请求处理或转发,相当于让请求流动起来。
二、责任链模式的实现
责任链模式结构图:
模式的实现:
//抽象处理者角色
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);
}
//具体处理者角色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("没有人处理该请求!");
}
}
}
}
//客户端实现
public class ChainOfResponsibilityPattern {
public static void main(String[] args) {
//组装责任链
Handler handler1 = new ConcreteHandler1();
Handler handler2 = new ConcreteHandler2();
handler1.setNext(handler2);
//提交请求
handler1.handleRequest("two");
}
}
/*
程序运行结果如下:
具体处理者2负责处理该请求!
*/
/*
在上面代码中,我们把消息硬编码为 String 类型,
而在真实业务中,消息是具备多样性的,可以是 int、String 或者自定义类型。
因此,在上面代码的基础上,可以对消息类型进行抽象 Request,增强了消息的兼容性。*/
三、案例实现
采购的分级审批问题也是责任链模式的一个应用典范。
我们知道,采购审批往往是分级进行的。
也就是说,其常常根据采购金额的不同由不同层次的主管人员来审批。
例如,
主任可以审批 5 万元以下(不包括 5 万元)的采购单,
副董事长可以审批 5 万元至 10 万元(不包括 10 万元)的采购单,
董事长可以审批 10 万元至 50 万元(不包括 50 万元)的采购单,
50 万元及以上的采购单就需要开董事会讨论决定。
此案例如图所示:
分析得出,在下图中,抽象类 Approver 充当抽象处理者,而 Director、VicePresident、President 和 Congress 则充当具体处理者,PurchaseRequest 充当请求消息类。
完整代码如下所示:
(1). 请求消息类
//请求消息类(采购单)
public class PurchaseRequest {
private double amount; //采购金额
private int number; //采购单编号
private String purpose; //采购目的
// 构造器
public PurchaseRequest(double amount, int number, String purpose) {
this.amount = amount;
this.number = number;
this.purpose = purpose;
}
// setter & getter
public void setAmount(double amount) {
this.amount = amount;
}
public double getAmount() {
return this.amount;
}
public void setNumber(int number) {
this.number = number;
}
public int getNumber() {
return this.number;
}
public void setPurpose(String purpose) {
this.purpose = purpose;
}
public String getPurpose() {
return this.purpose;
}
}
(2). 抽象处理者类 (抽象类)
// 抽象处理者(审批者类)
public abstract class Approver {
protected Approver successor; //定义后继处理对象
//设置后继者
public void setSuccessor(Approver successor) {
this.successor = successor;
}
//抽象请求处理方法
public abstract void processRequest(PurchaseRequest request);
}
(3). 具体处理者类 (抽象处理者的子类)
//主任类:具体处理者
public class Director extends Approver {
//具体请求处理方法
public void processRequest(PurchaseRequest request) {
if (request.getAmount() < 50000) {
System.out.println("主任" +"审批采购单:" + request.getNumber() + ",金额:" + request.getAmount() + "元,采购目的:" + request.getPurpose() + "。"); // 处理请求
}else {
this.successor.processRequest(request); // 转发请求
}
}
}
//副董事长类:具体处理者
public class VicePresident extends Approver {
//具体请求处理方法
public void processRequest(PurchaseRequest request) {
if (request.getAmount() < 100000) {
System.out.println("副董事长" + "审批采购单:" + request.getNumber() + ",金额:" + request.getAmount() + "元,采购目的:" + request.getPurpose() + "。"); // 处理请求
}else {
this.successor.processRequest(request); // 转发请求
}
}
}
//董事长类:具体处理者
public class President extends Approver {
//具体请求处理方法
public void processRequest(PurchaseRequest request) {
if (request.getAmount() < 500000) {
System.out.println("董事长" + "审批采购单:" + request.getNumber() + ",金额:" + request.getAmount() + "元,采购目的:" + request.getPurpose() + "。"); // 处理请求
}else {
this.successor.processRequest(request); // 转发请求
}
}
}
//董事会类:具体处理者
public class Congress extends Approver {
//具体请求处理方法
public void processRequest(PurchaseRequest request) {
System.out.println("召开董事会审批采购单:" + request.getNumber() + ",金额:" + request.getAmount() + "元,采购目的:" + request.getPurpose() + "。"); //处理请求
}
}
(4). 客户端测试代码
public class Client {
public static void main(String[] args) {
Approver director,vicePresident,president,congress;
director = new Director();
vicePresident = new VicePresident();
president = new President();
congress = new Congress();
//创建责任链
director.setSuccessor(vicePresident);
vicePresident.setSuccessor(president);
president.setSuccessor(congress);
//创建采购单,并从主任开始处理
PurchaseRequest pr1 = new PurchaseRequest(45000,10001,"购买倚天剑");
director.processRequest(pr1);
PurchaseRequest pr2 = new PurchaseRequest(60000,10002,"购买《葵花宝典》");
director.processRequest(pr2);
PurchaseRequest pr3 = new PurchaseRequest(160000,10003,"购买《金刚经》");
director.processRequest(pr3);
PurchaseRequest pr4 = new PurchaseRequest(800000,10004,"购买桃花岛");
director.processRequest(pr4);
}
}
/*
Output():
主任审批采购单:10001,金额:45000.0元,采购目的:购买倚天剑。
副董事长审批采购单:10002,金额:60000.0元,采购目的:购买《葵花宝典》。
董事长审批采购单:10003,金额:160000.0元,采购目的:购买《金刚经》。
召开董事会审批采购单:10004,金额:800000.0元,采购目的:购买桃花岛。
*/