设计模式
责任链模式
在现实生活中,经常会遇到一件事情经过多个对象处理的场景。比如公司员工请假可能需要经过多个领导批准,每个领导批准的范围不一样【项目经理2天以内,部门经理处理一周以内的,总经理处理更长时间的】;还比如产品的售后问题,一般客服处理,客服处理不了就交给经理处理,经理处理不了就只能交给总经理处理;还比如去银行的取款,一般小额度的取款可以直接在前台处理,如果取款量很大就有可能需要大堂经理处理,如果量大到大堂经理都处理不了就只能找他的上层领导了。像这种一条链式的,一个事件多个对象处理的就是责任链模式。
在 Java 应用程序中关于责任链模式的应用场景也很多,最典型的就是 JSP
和 Servlet
的 Filter
过滤器,当前过滤器处理后会将请求交给后续的过滤器继续处理,形成了一个过滤器链。还有 Struts2
的拦截器 也是使用的责任链模式实现的。
模式的定义
责任链模式【也叫职责链模式】(Chain of Responsibilit):使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这个对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止,它是一种对象行为模式。
责任链模式的优点:
- 当客户提交一个请求时,请求是沿链传递直至有一个 ConcreteHandler 对象负责处理它。
- 该模式使得一个对象无须知道到底是哪一个对象处理其请求,接收者和发送者都没有对方的明确信息,且链中的对象自己也并不知道链的结构,这也就大大降低了耦合度了。
- 增强了系统的可扩展性。可以根据需要增加新的请求处理类,满足开闭原则。
- 增强了给对象指派职责的灵活性。当工作流程发生变化,可以动态地改变链内的成员或者调动它们的次序,也可动态地新增或者删除成员。
- 职责链可简化对象的相互连接,它们仅需保持一个指向其后继者的引用,而不需保持它所有的候选接受者的引用,同时减少了条件判断语句的使用【 if···else / switch…case】。
- 责任分担。每个类只需要处理自己该处理的工作,不该处理的传递给下一个对象完成,明确各类的责任范围,符合类的单一职责原则。
缺点:
- 不能保证每个请求一定被处理。由于一个请求没有明确的接收者,所以一个请求极有可能到了链的末端都得不到处理,或者因为没有正确配置而得不到处理。
- 对比较长的职责链,请求的处理可能涉及多个处理对象,系统性能将受到一定影响。
- 职责链建立的合理性要靠客户端来保证,增加了客户端的复杂性,可能会由于职责链的错误设置而导致系统出错,如由循环引用造成的死循环。
模式的结构
责任链模式的主要角色如下:
- 抽象处理者角色(Handler):定义一个处理请求的接口,包含抽象处理方法和一个后继连接对象。
- 具体处理者角色(Concrete Handler):实现抽象处理者的处理方法,判断是否能够处理本次请求,如果能够处理则处理请求,否则将请求转发给后继连接。
- 客户端角色(Client):创建处理链,并向链头的具体处理对象提交请求,它不关心处理细节和请求的传递过程。
其 UML 图如下:
模式的使用
这里我们以 请假 和 申请聚餐费用 为例,介绍责任链模式的使用【仅为示例,请勿与生活实际情况挂钩】。
抽象处理者:管理类
Manager
,包含一个执行下一个管理者的引用 和 一个处理请求的抽象方法handleRequest
,其参数Request
封装了具体的请求内容。具体处理者:本例中共有3个具体处理者,分别为:
- 项目经理
ProjectManager
【具有500元及以下的费用审批和2日及以下的假期审批权限】;- 部门经理
DepartmentManager
【具有1000元及以下的费用审批和7日及以下的假期审批权限】;- 总经理
GeneralManager
【具有1500元及以下的费用审批和15日及以下的假期审批权限】。客户端:
ChainOfResponsibilityTest
负责创建各个具体处理者且组合成处理链,并向链头的具体梳理对象【ProjectManager
】提交请求。
/**
* 抽象处理者 - 管理者类
*/
public abstract class Manager {
/**
* 下一个管理者
*/
private Manager manager;
public void setNext(Manager manager) {
this.manager = manager;
}
public Manager getNext() {
return manager;
}
/**
* 处理请求的方法
*
* @param request
*/
public abstract void handleRequest(Request request);
}
/**
* 请求参数封装
*/
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class Request {
/**
* 请求类型 【leave - 请假、dineTogether - 聚餐】
*/
private String type;
/**
* 请假时长
*/
private int time;
/**
* 聚餐费用
*/
private int money;
}
/**
* 具体处理者 - 项目经理
*/
public class ProjectManager extends Manager {
@Override
public void handleRequest(Request request) {
if ("leave".equals(request.getType())) {
if (request.getTime() <= 2) {
System.out.println("项目经理批准您请假 " + request.getTime() + "天");
return;
}
} else if ("dineTogether".equals(request.getType())) {
if (request.getMoney() <= 500) {
System.out.println("项目经理批准了您的聚餐申请,金额为:" + request.getMoney() + "元");
return;
}
}
if (Objects.nonNull(getNext())) {
getNext().handleRequest(request);
} else {
System.out.println("您的请求无人批准【" + request.toString() + "】");
}
}
}
/**
* 具体处理者 - 部门经理
*/
public class DepartmentManager extends Manager{
@Override
public void handleRequest(Request request) {
if ("leave".equals(request.getType())) {
if (request.getTime() <= 7) {
System.out.println("部门经理批准您请假 " + request.getTime() + "天");
return;
}
} else if ("dineTogether".equals(request.getType())) {
if (request.getMoney() <= 1000) {
System.out.println("部门经理批准了您的聚餐申请,金额为:" + request.getMoney() + "元");
return;
}
}
if (Objects.nonNull(getNext())) {
getNext().handleRequest(request);
} else {
System.out.println("您的请求无人批准【" + request.toString() + "】");
}
}
}
/**
* 具体处理者 - 总经理
*/
public class GeneralManager extends Manager{
@Override
public void handleRequest(Request request) {
if ("leave".equals(request.getType())) {
if (request.getTime() <= 15) {
System.out.println("总经理批准您请假 " + request.getTime() + "天");
return;
}
} else if ("dineTogether".equals(request.getType())) {
if (request.getMoney() <= 1500) {
System.out.println("总经理批准了您的聚餐申请,金额为:" + request.getMoney() + "元");
return;
}
}
if (Objects.nonNull(getNext())) {
getNext().handleRequest(request);
} else {
System.out.println("您的请求无人批准【" + request.toString() + "】");
}
}
}
/**
* 客户端角色 - 责任链模式测试
*/
public class ChainOfResponsibilityTest {
public static void main(String[] args) {
Manager projectManager = new ProjectManager();
Manager departmentManager = new DepartmentManager();
Manager generalManager = new GeneralManager();
departmentManager.setNext(generalManager);
projectManager.setNext(departmentManager);
// 请假2天
projectManager.handleRequest(Request.builder().type("leave").time(2).build());
// 请假4天
projectManager.handleRequest(Request.builder().type("leave").time(4).build());
// 请假9天
projectManager.handleRequest(Request.builder().type("leave").time(9).build());
// 请假18天
projectManager.handleRequest(Request.builder().type("leave").time(18).build());
System.out.println();
// 申请聚餐金额 400
projectManager.handleRequest(Request.builder().type("dineTogether").money(400).build());
// 申请聚餐金额 800
projectManager.handleRequest(Request.builder().type("dineTogether").money(800).build());
// 申请聚餐金额 1200
projectManager.handleRequest(Request.builder().type("dineTogether").money(1200).build());
// 申请聚餐金额 1800
projectManager.handleRequest(Request.builder().type("dineTogether").money(1800).build());
}
}
运行程序,结果如下:
纯的与不纯的责任链模式
- 纯的职责链模式:一个请求必须被某一个处理者对象所接收,且一个具体处理者对某个请求的处理只能采用以下两种行为之一:自己处理(承担责任);把责任推给下家处理。
- 不纯的职责链模式:允许出现某一个具体处理者对象在承担了请求的一部分责任后又将剩余的责任传给下家的情况,且一个请求可以最终不被任何接收端对象所接收。
模式的应用场景
- 多个对象可以处理一个请求,但具体由哪个对象处理该请求在运行时自动确定。
- 可动态指定一组对象处理请求,或添加新的处理者。
- 需要在不明确指定请求处理者的情况下,向多个处理者中的一个提交请求。