责任链模式

责任链模式

责任链模式(Chain of Responsibility Pattern)是一种行为型设计模式,它用于将请求的发送者和接收者解耦,使多个对象都有机会处理请求。这种模式建立在一个处理对象的链上,每个处理对象都可以选择处理请求或者将请求传递给链上的下一个处理对象。

1、责任链模式角色

在 Java 中,责任链模式的实现通常包括以下几个要素:

  1. 抽象处理者(Handler):定义一个处理请求的接口,通常包括一个处理方法(例如:handleRequest())和一个设置下一个处理者的方法(例如:setNextHandler())。抽象处理者可以是一个接口或者抽象类。
  2. 具体处理者(ConcreteHandler):实现抽象处理者接口,具体处理不同的请求。如果当前处理者无法处理请求,它可以将请求传递给下一个处理者。
  3. 责任链(Chain ofResponsibility):将一系列的处理者连接成一个链,形成处理请求的链式结构。通常在客户端代码中构建这个责任链,并将请求从链的开头传递给第一个处理者。

2、责任链模式适用业务场景

  1. 日志记录系统:在系统中实现日志记录功能时,可以使用责任链模式。不同的日志级别(如调试、信息、警告、错误)可以由不同的处理器来处理,从而根据需要将日志记录到不同的目标(文件、数据库、控制台等)。
  2. 审批流程:在企业应用中,审批流程可能涉及多个层级的审批,每个层级的审批者都可以决定是否通过审批。责任链模式可以用于构建这样的审批流程,其中每个处理器代表一个审批者,如果一个审批者无法处理,请求将传递给下一个审批者。
  3. 安全认证:在安全认证中,可以使用责任链模式来实现多层级的认证机制。每个认证处理器可以负责一个特定的认证方法(如用户名密码、指纹、令牌等)。如果一个认证处理器无法通过认证,系统可以继续尝试下一个处理器。
  4. HTTP 请求处理:在 Web 应用程序中,HTTP
    请求处理可以分成多个环节,例如身份验证、授权、输入验证、缓存等。责任链模式可以用于将每个环节拆分成一个处理器,并将请求从一个处理器传递到下一个处理器。
  5. 异常处理链:在处理异常时,可能需要一系列处理步骤来处理不同类型的异常。责任链模式可以用于将异常处理逻辑分解成多个处理器,每个处理器负责处理一种类型的异常。
  6. 请求过滤器:在 Web
    开发中,请求过滤器可以用于对请求进行预处理,例如请求参数验证、安全检查等。责任链模式可以用于将不同的过滤逻辑拆分成多个处理器,依次对请求进行处理。
  7. 游戏闯关系统:在游戏中,角色的闯关系统可以采用责任链模式。每层关卡可以由不同的处理器来处理,如果一个处理器处理完成当前关卡,结果打到过关条件,系统可以使用下一个处理器进行下一关处理。

总之,责任链模式在任何需要将处理逻辑拆分成独立步骤,并且这些步骤可以灵活组合的情况下都是有用的。它帮助减少耦合,使代码更加可扩展和可维护。

3、游戏闯关系统责任链模式的应用

需求描述

假设现在有一个闯关游戏,进入下一关的条件是上一关的分数要高于 xx:

游戏一共 3 个关卡

进入第二关需要第一关的游戏得分大于等于 90

进入第三关需要第二关的游戏得分大于等于 80

普通业务代码实现

cpp 复制代码
//第一关
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();
        //第一关的分数大于等于90则进入第二关
        if(firstScore >= 90){
            int secondScore = secondPassHandler.handler();
            //第二关的分数大于等于80则进入第二关
            if(secondScore >= 80){
                thirdPassHandler.handler();
            }
        }
    }
}

那么如果这个游戏有 99 关,我们的代码很可能就会写成这个样子:

cpp 复制代码
if(第1关通过){
    // 第2关 游戏
    if(第2关通过){
        // 第3关 游戏
        if(第3关通过){
           // 第4关 游戏
            if(第4关通过){
                // 第5关 游戏
                if(第5关通过){
                    // 第6关 游戏
                    if(第6关通过){
                        //...
                    }
                }
            } 
        }
    }
}

这种代码不仅冗余,并且当我们要将某两关进行调整时会对代码非常大的改动,这种操作的风险是很高的,因此,该写法非常糟糕。

责任链改造代码

如何解决这个问题,我们可以通过链表将每一关连接起来,形成责任链的方式,第一关通过后是第二关,第二关通过后是第三关...

这样客户端就不需要进行多重 if 的判断了:

cpp 复制代码
public abstract class Handler {

    /**
     * 下一关用当前抽象接口来接收
     */
    protected Handler next;

    public void setNext(Handler next) {
        this.next = next;
    }

    public abstract int handler();
}
cpp 复制代码
public class FirstPassHandler extends Handler{

    final int firstPassScore = 90;
    private int play(){
        return firstPassScore;
    }

    @Override
    public int handler(){
        System.out.println("第一关-->FirstPassHandler");
        int score = play();
        if(score >= firstPassScore){
            //分数>=firstPassScore 并且存在下一关才进入下一关
            if(this.next != null){
                return this.next.handler();
            }
        }
        return score;
    }
}
cpp 复制代码
package com.lf.java.designpattern.chain;

public class SecondPassHandler extends Handler{

    final int SecondPassScore = 80;

    private int play(){
        return SecondPassScore;
    }

    public int handler(){
        System.out.println("第二关-->SecondPassHandler");

        int score = play();
        if(score >= SecondPassScore){
            //分数>=SecondPassScore 并且存在下一关才进入下一关
            if(this.next != null){
                return this.next.handler();
            }
        }

        return score;
    }
}
cpp 复制代码
package com.lf.java.designpattern.chain;

public class ThirdPassHandler extends Handler{

    final int SecondPassScore = 70;

    private int play(){
        return SecondPassScore;
    }

    public int handler(){
        System.out.println("第三关-->ThirdPassHandler");
        int score = play();
        if(score >= SecondPassScore){
            //分数>=SecondPassScore 并且存在下一关才进入下一关
            if(this.next != null){
                return this.next.handler();
            }
        }
        return score;
    }
}
cpp 复制代码
package com.lf.java.designpattern.chain;

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();

    }
}

改造完成的代码请求会从链的开头传递到每个处理器,根据请求的内容,每个处理器都可以选择处理请求或者将请求传递给下一个处理器。这样的设计使得责任链可以根据需要动态地调整和扩展。

但是还不能自动化的添加对应的链之间的关系。

责任链工厂改造代码

对于上面的请求链,我们也可以把这个关系维护到配置文件中或者一个枚举中。将使用枚举来动态的配置请求链并且将每个请求者形成一条调用链。

cpp 复制代码
@Data
@AllArgsConstructor
@NoArgsConstructor
@EqualsAndHashCode
@ToString
public class PassEntity {
    /** 处理器顺序id */
    private Integer handlerId;

    /** 业务处理器名称*/
    private String name;

    /** 全限定类名 */
    private String conference;

    /** 前置处理器 */
    private Integer preHandlerId;
    /** 前置处理器 */
    private Integer nextHandlerId;
}
cpp 复制代码
public enum PassEnum {
    // handlerId, 拦截者名称,全限定类名,preHandlerId,nextHandlerId
    API_HANDLER(new PassEntity(1, "第一关", "com.lf.java.design.pattern.chain.FirstPassHandler", null, 2)),
    BLACKLIST_HANDLER(new PassEntity(2, "第二关", "com.lf.java.design.pattern.chain.SecondPassHandler", 1, 3)),
    SESSION_HANDLER(new PassEntity(3, "第三关", "com.lf.java.design.pattern.chain.ThirdPassHandler", 2, null)),
    ;

    PassEntity passEntity;

    public PassEntity getPassEntity() {
        return passEntity;
    }

    PassEnum(PassEntity passEntity) {
        this.passEntity = passEntity;
    }
}
cpp 复制代码
public interface IPassService {

    /**
     * 根据 handlerId 获取配置项
     * @param handlerId
     * @return
     */
    PassEntity getPassEntity(Integer handlerId);

    /**
     * 获取第一个处理者
     * @return
     */
    PassEntity getFirstPassEntity();
}
cpp 复制代码
package com.lf.java.designpattern.chain;

import java.util.HashMap;
import java.util.Map;

public class PassServiceImpl implements IPassService {

    /**
     * 初始化,将枚举中配置的handler初始化到map中,方便获取
     */
    private static Map<Integer, PassEntity> passEntityMap = new HashMap<>();

    static {
        PassEnum[] values = PassEnum.values();
        for (PassEnum value : values) {
            PassEntity passEntity = value.getPassEntity();
            passEntityMap.put(passEntity.getHandlerId(), passEntity);
        }
    }

    @Override
    public PassEntity getPassEntity(Integer handlerId) {
        return passEntityMap.get(handlerId);
    }

    @Override
    public PassEntity getFirstPassEntity() {
        for (Map.Entry<Integer, PassEntity> entry : passEntityMap.entrySet()) {
            PassEntity value = entry.getValue();
            //  没有上一个handler的就是第一个
            if (value.getPreHandlerId() == null) {
                return value;
            }
        }
        return null;
    }
}
cpp 复制代码
package com.lf.java.designpattern.chain;

public class PassHandlerEnumFactory {

    private static IPassService passService = new PassServiceImpl();

    // 提供静态方法,获取第一个handler
    public static Handler getFirstPassHandler() {

        PassEntity firstPassEntity = passService.getFirstPassEntity();
        Handler firstPassHandler = newPassHandler(firstPassEntity);
        if (firstPassHandler == null) {
            return null;
        }

        PassEntity tempPassEntity = firstPassEntity;
        Integer nextHandlerId = null;
        Handler tempPassHandler = firstPassHandler;
        // 迭代遍历所有handler,以及将它们链接起来
        while ((nextHandlerId = tempPassEntity.getNextHandlerId()) != null) {
            PassEntity PassEntity = passService.getPassEntity(nextHandlerId);
            Handler PassHandler = newPassHandler(PassEntity);
            tempPassHandler.setNext(PassHandler);
            tempPassHandler = PassHandler;
            tempPassEntity = PassEntity;
        }
    // 返回第一个handler
        return firstPassHandler;
    }
    /**
     * 反射实体化具体的处理者
     * @param firstPassEntity
     * @return
     */
    private static Handler newPassHandler(PassEntity firstPassEntity) {
        // 获取全限定类名
        String className = firstPassEntity.getConference();
        try {
            // 根据全限定类名,加载并初始化该类,即会初始化该类的静态段
            Class<?> clazz = Class.forName(className);
            return (Handler) clazz.newInstance();
        } catch (ClassNotFoundException | IllegalAccessException | InstantiationException e) {
            e.printStackTrace();
        }
        return null;
    }


}

测试业务类

cpp 复制代码
public class PassHandlerClient {
    public static void main(String[] args) {
        Handler firstPassHandler = PassHandlerEnumFactory.getFirstPassHandler();
        firstPassHandler.handler();
    }
}

运行结果:

cpp 复制代码
第一关-->FirstPassHandler
第二关-->SecondPassHandler
第三关-->ThirdPassHandler

这样通过动态的配置请求链就可以自动将每个请求者形成一条调用链。

还有更为复杂的链的形成,比如业务的链里面有复合链,而复合链又是普通的功能链组成的。这种责任链工厂方式更能充分体现出代码设计优势。

相关推荐
小白不太白95018 小时前
设计模式之 责任链模式
python·设计模式·责任链模式
吾与谁归in19 小时前
【C#设计模式(14)——责任链模式( Chain-of-responsibility Pattern)】
设计模式·c#·责任链模式
你知道烟火吗3 天前
springboot:责任链模式实现多级校验
spring boot·后端·责任链模式
拉里小猪的迷弟6 天前
设计模式-行为型-常用-2:职责链模式、状态模式、迭代器模式
java·设计模式·迭代器模式·状态模式·责任链模式
G皮T7 天前
【设计模式】行为型模式(三):责任链模式、状态模式
java·设计模式·状态模式·编程·责任链模式·state
前端拾光者10 天前
前端开发设计模式——责任链模式
设计模式·责任链模式
老攀呀14 天前
责任链模式 Chain of Responsibility
责任链模式
萌面小侠Plus14 天前
Android笔记(三十五):用责任链模式封装一个App首页Dialog管理工具
android·dialog·笔记·kotlin·责任链模式
后端小张14 天前
设计模式讲解02—责任链模式(Chain)
java·开发语言·设计模式·责任链模式
wrx繁星点点14 天前
责任链模式(Chain of Responsibility Pattern)详解
java·开发语言·设计模式·责任链模式