责任链模式是一种行为设计模式, 允许你将请求沿着处理者链进行发送。收到请求后, 每个处理者均可对请求进行处理, 或将其传递给链上的下个处理者。
在责任链模式中,客户只需要将请求发送到责任链上即可,无须关心请求的处理细节和请求的传递过程,所以责任链将请求的发送者和请求的处理者解耦了。
责任链的使用场景还是比较多的
多条件流程判断:权限控制
ERP 系统流程审批:总经理、人事经理、项目经理
Java 过滤器 的底层实现 Filter
如果不使用该设计模式,那么当需求有所改变时,就会使得代码臃肿或者难以维护,例如下面的例子
假设现在有一个闯关游戏,进入下一关的条件是上一关的分数要高于xx
游戏一共 3 个关卡
进入第二关需要第一关的游戏得分大于等于 80
进入第三关需要第二关的游戏得分大于等于 90
//第一关
public class FirstPassHandler {
public int handler(){
System.out.println("第一关-->FirstPassHandler");
return 80;
}
}
//第二关
public class SecondPassHandler {
public int handler(){
System.out.println("第二关-->SecondPassHandler");
return 90;
}
}
//第三关
public class ThirdPassHandler {
public int handler(){
System.out.println("第三关-->ThirdPassHandler,这是最后一关啦");
return 95;
}
}
//客户端
public class HandlerClient {
public static void main(String[] args) {
FirstPassHandler firstPassHandler = new FirstPassHandler();//第一关
SecondPassHandler secondPassHandler = new SecondPassHandler();//第二关
ThirdPassHandler thirdPassHandler = new ThirdPassHandler();//第三关
int firstScore = firstPassHandler.handler();
//第一关的分数大于等于80则进入第二关
if(firstScore >= 80){
int secondScore = secondPassHandler.handler();
//第二关的分数大于等于90则进入第二关
if(secondScore >= 90){
thirdPassHandler.handler();
}
}
}
}
如果这个游戏有100关,我们的代码很可能就会写成这个样子
if(第1关通过){
// 第2关 游戏
if(第2关通过){
// 第3关 游戏
if(第3关通过){
// 第4关 游戏
if(第4关通过){
// 第5关 游戏
if(第5关通过){
// 第6关 游戏
if(第6关通过){
//...
}
}
}
}
}
}
这种代码不仅冗余,并且当我们要将某两关进行调整时会对代码非常大的改动,这种操作的风险是很高的,因此,该写法非常糟糕。
1.编写AbstractHandler 抽象类,用于责任链中的对象传递
2.编写关卡1,2,3 ,继承抽象类
public abstract class AbstractHandler {
/**
* 下一关用当前抽象类来接收
*/
protected AbstractHandler next;
public void setNext(AbstractHandler next) {
this.next = next;
}
public abstract int handler();
}
public class FirstPassHandler extends AbstractHandler{
private int play(){
return 80;
}
@Override
public int handler(){
System.out.println("第一关-->FirstPassHandler");
int score = play();
if(score >= 80){
//分数>=80 并且存在下一关才进入下一关
if(this.next != null){
return this.next.handler();
}
}
return score;
}
}
public class SecondPassHandler extends AbstractHandler{
private int play(){
return 90;
}
public int handler(){
System.out.println("第二关-->SecondPassHandler");
int score = play();
if(score >= 90){
//分数>=90 并且存在下一关才进入下一关
if(this.next != null){
return this.next.handler();
}
}
return score;
}
}
public class ThirdPassHandler extends AbstractHandler{
private int play(){
return 95;
}
public int handler(){
System.out.println("第三关-->ThirdPassHandler");
int score = play();
if(score >= 95){
//分数>=95 并且存在下一关才进入下一关
if(this.next != null){
return this.next.handler();
}
}
return score;
}
}
public class HandlerClient {
public static void main(String[] args) {
FirstPassHandler firstPassHandler = new FirstPassHandler();//第一关
SecondPassHandler secondPassHandler = new SecondPassHandler();//第二关
ThirdPassHandler thirdPassHandler = new ThirdPassHandler();//第三关
// 和上面没有更改的客户端代码相比,只有这里的set方法发生变化,其他都是一样的
firstPassHandler.setNext(secondPassHandler);//第一关的下一关是第二关
secondPassHandler.setNext(thirdPassHandler);//第二关的下一关是第三关
//说明:因为第三关是最后一关,因此没有下一关
//从第一个关卡开始
firstPassHandler.handler();
}
}
对于上面的请求链,我们也可以把这个关系维护到配置文件中或者一个枚举中。我将使用枚举来教会大家怎么动态的配置请求链并且将每个请求者形成一条调用链。
public enum GatewayEnum {
// handlerId, 拦截者名称,全限定类名,preHandlerId,nextHandlerId
API_HANDLER(new GatewayEntity(1, "api接口限流",
"cn.dgut.design.chain_of_responsibility.GateWay.impl.ApiLimitGatewayHandler", null, 2)),
BLACKLIST_HANDLER(new GatewayEntity(2, "黑名单拦截",
"cn.dgut.design.chain_of_responsibility.GateWay.impl.BlacklistGatewayHandler", 1, 3)),
SESSION_HANDLER(new GatewayEntity(3, "用户会话拦截",
"cn.dgut.design.chain_of_responsibility.GateWay.impl.SessionGatewayHandler", 2, null));
GatewayEntity gatewayEntity;
public GatewayEntity getGatewayEntity() {
return gatewayEntity;
}
GatewayEnum(GatewayEntity gatewayEntity) {
this.gatewayEntity = gatewayEntity;
}
}
public class GatewayEntity {
private String name;
private String conference;
private Integer handlerId;
private Integer preHandlerId;
private Integer nextHandlerId;
}
public interface GatewayDao {
/**
* 根据 handlerId 获取配置项
* @param handlerId
* @return
*/
GatewayEntity getGatewayEntity(Integer handlerId);
/**
* 获取第一个处理者
* @return
*/
GatewayEntity getFirstGatewayEntity();
}
public class GatewayImpl implements GatewayDao {
/**
* 初始化,将枚举中配置的handler初始化到map中,方便获取
*/
private static Map gatewayEntityMap = new HashMap<>();
static {
GatewayEnum[] values = GatewayEnum.values();
for (GatewayEnum value : values) {
GatewayEntity gatewayEntity = value.getGatewayEntity();
gatewayEntityMap.put(gatewayEntity.getHandlerId(), gatewayEntity);
}
}
@Override
public GatewayEntity getGatewayEntity(Integer handlerId) {
return gatewayEntityMap.get(handlerId);
}
@Override
public GatewayEntity getFirstGatewayEntity() {
for (Map.Entry entry : gatewayEntityMap.entrySet()) {
GatewayEntity value = entry.getValue();
// 没有上一个handler的就是第一个
if (value.getPreHandlerId() == null) {
return value;
}
}
return null;
}
}
public class GatewayHandlerEnumFactory {
private static GatewayDao gatewayDao = new GatewayImpl();
// 提供静态方法,获取第一个handler
public static GatewayHandler getFirstGatewayHandler() {
GatewayEntity firstGatewayEntity = gatewayDao.getFirstGatewayEntity();
GatewayHandler firstGatewayHandler = newGatewayHandler(firstGatewayEntity);
if (firstGatewayHandler == null) {
return null;
}
GatewayEntity tempGatewayEntity = firstGatewayEntity;
Integer nextHandlerId = null;
GatewayHandler tempGatewayHandler = firstGatewayHandler;
// 迭代遍历所有handler,以及将它们链接起来
while ((nextHandlerId = tempGatewayEntity.getNextHandlerId()) != null) {
GatewayEntity gatewayEntity = gatewayDao.getGatewayEntity(nextHandlerId);
GatewayHandler gatewayHandler = newGatewayHandler(gatewayEntity);
tempGatewayHandler.setNext(gatewayHandler);
tempGatewayHandler = gatewayHandler;
tempGatewayEntity = gatewayEntity;
}
// 返回第一个handler
return firstGatewayHandler;
}
/**
* 反射实体化具体的处理者
* @param firstGatewayEntity
* @return
*/
private static GatewayHandler newGatewayHandler(GatewayEntity firstGatewayEntity) {
// 获取全限定类名
String className = firstGatewayEntity.getConference();
try {
// 根据全限定类名,加载并初始化该类,即会初始化该类的静态段
Class> clazz = Class.forName(className);
return (GatewayHandler) clazz.newInstance();
} catch (ClassNotFoundException | IllegalAccessException | InstantiationException e) {
e.printStackTrace();
}
return null;
}
}
public class GetewayClient {
public static void main(String[] args) {
GetewayHandler firstGetewayHandler = GetewayHandlerEnumFactory.getFirstGetewayHandler();
firstGetewayHandler.handler();
}
}
职责链模式可分为纯的职责链模式和不纯的职责链模式两种
一个纯的职责链模式要求一个具体处理者对象只能在两个行为中选择一个:要么承担全部责任,要么将责任推给下家,不允许出现某一个具体处理者对象在承担了一部分或全部责任后又将责任向下传递的情况。而且在纯的职责链模式中,要求一个请求必须被某一个处理者对象所接收,不能出现某个请求未被任何一个处理者对象处理的情况。在前面的采购单审批实例中应用的是纯的职责链模式。
不纯的职责链模式
在一个不纯的职责链模式中允许某个请求被一个具体处理者部分处理后再向下传递,或者一个具体处理者处理完某请求后其后继处理者可以继续处理该请求,而且一个请求可以最终不被任何处理者对象所接收。Java AWT 1.0中的事件处理模型应用的是不纯的职责链模式,其基本原理如下:由于窗口组件(如按钮、文本框等)一般都位于容器组件中,因此当事件发生在某一个组件上时,先通过组件对象的handleEvent()方法将事件传递给相应的事件处理方法,该事件处理方法将处理此事件,然后决定是否将该事件向上一级容器组件传播;上级容器组件在接到事件之后可以继续处理此事件并决定是否继续向上级容器组件传播,如此反复,直到事件到达顶层容器组件为止;如果一直传到最顶层容器仍没有处理方法,则该事件不予处理。每一级组件在接收到事件时,都可以处理此事件,而不论此事件是否在上一级已得到处理,还存在事件未被处理的情况。显然,这就是不纯的职责链模式,早期的Java AWT事件模型(JDK 1.0及更早)中的这种事件处理机制又叫事件浮升(Event Bubbling)机制。从Java.1.1以后,JDK使用观察者模式代替职责链模式来处理事件。目前,在JavaScript中仍然可以使用这种事件浮升机制来进行事件处理。