在很多情况下可以处理某个请求与的对象并不止一个,例如大学里的奖学金审批,先要将审批表交给辅导员签字,辅导员再交给系主任签字,系主任再给院长签字,最后可能就是校长来审批。学生无需与其他审批者交互,只需等待结果即可。在审批过程中如果某一个审批者认为不符合条件,则请求终止,否则传递给下一个审批者。
定义
避免将一个请求的发送者与接收者耦合在一起,让多个对象都有机会处理请求。将接收请求的对象连接成一条链,并且沿着这条链传递请求,直到有一个对象能够处理它为止。
结构
Handler(抽象处理者)
它定义了一个处理请求的接口,一般设计为抽象类,由于不同的具体处理者处理请求的凡是不同,因此在其中定义了抽象请求处理方法。每一个处理者的下家还是一个处理者,故在抽象处理者中定义了一个抽象处理者类型的对象,通过该引用处理者可以连成一条链。
ConcreteHandler(具体处理者)
它是抽象处理者的子类,可以处理用户请求,在具体处理者类中实现了抽象处理者中的抽象请求处理方法,在处理请求之前需要进行判断,看是否有相应的处理权限,如果可以处理请求就处理它,否则将请求转发给后继者;在具体处理者中可以访问链中的下一个对象,以便请求的转发。
举例说明
某企业SCM系统中包含一采购审批子系统。金额<5万,需要主任审批,<10万需要副董事长审批,<50万需要董事长审批,>50万需要召开董事会决定。
1.请求类
// 采购单类 - 充当请求类
@Getter@Setter
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;
}
}
2.抽象处理者
// 抽象处理者 - 审批者类
public abstract class Approver {
// 后继对象
protected Approver successor;
// 审判者姓名
protected String name;
public Approver(String name) {
this.name = name;
}
public void setSuccessor(Approver successor) {
this.successor = successor;
}
// 抽象请求处理方法
public abstract void processRequest(PurchaseRequest request);
}
3.具体处理者
// 具体处理者 - 主任类
public class Director extends Approver{
public Director(String name) {
super(name);
}
@Override
public void processRequest(PurchaseRequest request) {
if(request.getAmount() < 50000) {
// 处理请求
System.out.println("主任" + this.name + "审批采购单:" + request.getNumber() +
",金额:" + request.getAmount() + "元,采购目的:" + request.getPurpose());
}else {
// 转发请求
this.successor.processRequest(request);
}
}
}
// 具体处理者 - 副董事长类
public class VicePresident extends Approver{
public VicePresident(String name) {
super(name);
}
@Override
public void processRequest(PurchaseRequest request) {
if(request.getAmount() < 100000) {
// 处理请求
System.out.println("副董事长" + this.name + "审批采购单:" + request.getNumber() +
",金额:" + request.getAmount() + "元,采购目的:" + request.getPurpose());
}else {
// 转发请求
this.successor.processRequest(request);
}
}
}
//具体处理者 - 董事长类
public class President extends Approver {
public President(String name){
super(name);
}
@Override
public void processRequest(PurchaseRequest request) {
if(request.getAmount() < 500000) {
// 处理请求
System.out.println("董事长" + this.name + "审批采购单:" + request.getNumber() +
",金额:" + request.getAmount() + "元,采购目的:" + request.getPurpose());
}else {
// 转发请求
this.successor.processRequest(request);
}
}
}
// 具体处理者 - 董事会类
public class Congress extends Approver{
public Congress(String name){
super(name);
}
@Override
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);
}
}
5.运行截图
由于链的创建过程由客户端负责,因此增加新的具体处理者类对原有类库无任何英雄,无需修改已有类的源代码,符合开闭原则。在客户端代码中,如果要将新的具体请求处理者应用在系统中,需要创建新的具体处理者对象,然后将该对象加入职责链中即可。
纯与不纯的职责链模式
1.纯的职责链模式
它要求一个具体处理者对象只能在两个行为中选择一个,要么承担全部责任,要么将责任推给下家,不允许出现某一个具体处理者对象在承担了一部分或全部责任后又将责任向下传递的情况。而且在纯的职责链模式中要求一个请求必须被某一个处理者对象所接收,不能出现某个请求未被任何一个处理者对象处理的情况。上诉例子是一个纯的职责链模式。
2.不纯的职责链模式
它允许某个请求被具体处理者处理部分后再向下传递,或者一个具体处理者处理完某请求后其后继者可以继续处理该请求,而且一个请求可以最终不被任何处理者对象所接受并处理。
在Java AWT1.0中的事件处理模型应用的就是不纯的职责链模式。其实JavaScript中的事件冒泡就是这种机制。
优点
1.它使得一个对象无需知道是其他哪一个对象处理其请求,对象仅需知道该请求会被处理即可。由客户端负责链的创建,降低了系统的耦合度。
2.简化了对象之间的相互连接。
3.在分派职责时,职责链可以带来更多灵活性,可以通过在运行时对该链进行动态的增加或修改来增加或改变处理一个请求的职责。
4.符合开闭原则。
缺点
1.一个请求可能一直到链末端都得不到处理,或者因为职责链没有正确配置而不被处理。
2.对于长的职责链,请求的处理涉及多个处理对象,系统将受到一定影响,进行调试时也不方便。
3.如果键链不当,可能会造成循环调用,导致系统陷入死循环。