巧妙的运用责任链模式,让你的代码高出一个逼格!

简介: 什么是责任链模式?(Chain of Responsibility Pattern),简单的说,为请求者和接受者之间创建一条对象处理链路,避免请求发送者与接收者耦合在一起!

一、介绍

什么是责任链模式?(Chain of Responsibility Pattern),简单的说,为请求者和接受者之间创建一条对象处理链路,避免请求发送者与接收者耦合在一起

例如,如下图:

0.jpg

从设计的角度看,责任链模式涉及到四个角色:

  • 请求角色:可以是外部的请求或者内部的请求,最终体现就是一个请求数据体;
  • 抽象处理器角色:定义处理的一些基本的规范;
  • 具体处理器角色:实现或者继承抽象处理器,完成具体的计算任务;
  • 接着角色:用于接受请求数据最终的处理结果;

下面我们一起来看看具体的实际应用!

二、示例

在实际开发中,经常避免不了会与其他公司进行接口对接,绝大部分请求参数都是经过加密处理再发送到互联网上,下面我们以对请求参数进行验证、封装处理为例,来诠释责任链模式的玩法,实现过程如下!

  • 我们先编写一个加密工具类,采用AES加密算法
public class AESUtil {
    private static Logger log = LoggerFactory.getLogger(AESUtil.class);
    private static final String AES = "AES";
    private static final String AES_CVC_PKC = "AES/CBC/PKCS7Padding";
    static {
        Security.addProvider(new BouncyCastleProvider());
    }
    /**
     * 加密
     * @param content
     * @param key
     * @return
     * @throws Exception
     */
    public static String encrypt(String content, String key) {
        try {
            SecretKeySpec secretKeySpec = new SecretKeySpec(key.getBytes(), AES);
            Cipher cipher = Cipher.getInstance(AES_CVC_PKC);
            IvParameterSpec iv = new IvParameterSpec(new byte[16]);
            cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, iv);
            byte[] encrypted = cipher.doFinal(content.getBytes());
            return Base64.getEncoder().encodeToString(encrypted);
        }  catch (Exception e) {
            log.warn("AES加密失败,参数:{},错误信息:{}", content, ExceptionUtils.getStackTrace(e));
            return "";
        }
    }
    /**
     * 解密
     * @param content
     * @param key
     * @return
     * @throws Exception
     */
    public static String decrypt(String content, String key) {
        try {
            SecretKeySpec secretKeySpec = new SecretKeySpec(key.getBytes(), AES);
            Cipher cipher = Cipher.getInstance(AES_CVC_PKC);
            IvParameterSpec iv = new IvParameterSpec(new byte[16]);
            cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, iv);
            byte[] encrypted = Base64.getDecoder().decode(content);
            byte[] original = cipher.doFinal(encrypted);
            return new String(original, "UTF-8");
        } catch (Exception e) {
            log.warn("AES解密失败,参数:{},错误信息:{}", content, ExceptionUtils.getStackTrace(e));
            return "";
        }
    }
    public static void main(String[] args) throws Exception {
        String key = "1234567890123456";
        String content = "{\"userCode\":\"zhangsan\",\"userPwd\":\"123456\"}";
        String encryptContext = encrypt(content, "1234567890123456");
        System.out.println("加密后的内容:" + encryptContext);
        String decryptContext = decrypt(encryptContext, key);
        System.out.println("解密后的内容:" + decryptContext);
    }
}

执行结果如下:

加密后的内容:5ELORDsYKxCz6Ec377udct7dBMI74ZtJDCFL4B3cpoBsPC8ILH/aiaRFnZa/oTC5
解密后的内容:{"userCode":"zhangsan","userPwd":"123456"}

其中加密后的内容可以看作为请求者传过来的参数!

  • 同时,再创建一个上下文实体类ServiceContext,用于数据记录
/**
 * 上下文
 */
public class ServiceContext {
    /**
     * 请求参数
     */
    private String requestParam;
    /**
     * 解密后的数据
     */
    private String jsonData;
    /**
     * 用户账号
     */
    private String userCode;
    /**
     * 用户密码
     */
    private String userPwd;
    //省略set\get
    public ServiceContext() {
    }
    public ServiceContext(String requestParam) {
        this.requestParam = requestParam;
    }
}
  • 然后,创建一个处理器接口HandleIntercept
public interface HandleIntercept {
    /**
     * 对参数进行处理
     * @param context
     * @return
     */
    ServiceContext handle(ServiceContext context);
}
  • 紧接着,创建两个处理器实现类,用于参数解密、业务数据验证
/**
 * 解密请求数据
 */
public class DecodeDataHandle implements HandleIntercept {
    private String key = "1234567890123456";
    @Override
    public ServiceContext handle(ServiceContext context) {
        String jsonData = AESUtil.decrypt(context.getRequestParam(), key);
        if(StringUtils.isEmpty(jsonData)){
            throw new IllegalArgumentException("解密失败");
        }
        context.setJsonData(jsonData);
        return context;
    }
}
/**
 * 验证业务数据并封装
 */
public class ValidDataHandle implements HandleIntercept {
    @Override
    public ServiceContext handle(ServiceContext context) {
        String jsonData = context.getJsonData();
        JSONObject jsonObject = JSONObject.parseObject(jsonData);
        if(!jsonObject.containsKey("userCode")){
            throw new IllegalArgumentException("userCode不能为空");
        }
        context.setUserCode(jsonObject.getString("userCode"));
        if(!jsonObject.containsKey("userPwd")){
            throw new IllegalArgumentException("userPwd不能为空");
        }
        context.setUserPwd(jsonObject.getString("userPwd"));
        return context;
    }
}
  • 最后创建一个处理链路管理器HandleChain
/**
 * 请求处理链路管理器
 */
public class HandleChain {
    private List<HandleIntercept> handleInterceptList = new ArrayList<>();
    /**
     * 添加处理器
     * @param handleIntercept
     */
    public void addHandle(HandleIntercept handleIntercept){
        handleInterceptList.add(handleIntercept);
    }
    /**
     * 执行处理
     * @param context
     * @return
     */
    public ServiceContext execute(ServiceContext context){
        if(!handleInterceptList.isEmpty()){
            for (HandleIntercept handleIntercept : handleInterceptList) {
                context =handleIntercept.handle(context);
            }
        }
        return context;
    }
}
  • 写完之后,我们编写一个测试类ChainClientTest
public class ChainClientTest {
    public static void main(String[] args) {
        //获取请求参数
        String requestParam = "5ELORDsYKxCz6Ec377udct7dBMI74ZtJDCFL4B3cpoBsPC8ILH/aiaRFnZa/oTC5";
        //封装请求参数
        ServiceContext serviceContext = new ServiceContext(requestParam);
        //添加处理链路
        HandleChain handleChain = new HandleChain();
        handleChain.addHandle(new DecodeDataHandle());//解密处理
        handleChain.addHandle(new ValidDataHandle());//数据验证处理
        //执行处理链,获取处理结果
        serviceContext = handleChain.execute(serviceContext);
        System.out.println("处理结果:" + JSONObject.toJSONString(serviceContext));
    }
}

执行之后结果如下:

处理结果:{"jsonData":"{\"userCode\":\"zhangsan\",\"userPwd\":\"123456


1.jpg

可以很清晰的看到,从请求者发送数据经过处理器链路之后,数据都封装到上下文中去了!

如果想继续验证用户和密码是否合法,可以继续添加新的处理器,即可完成数据的处理验证!

如果是传统的方法,可能就是多个if,进行嵌套,类似如下:

if(condition){
    if(condition){
        if(condition){
   //业务处理
        }
    }
}

这种模式,最大的弊端就是可读性非常差,而且代码不好维护!

而责任链是从接口层进行封装处理和判断,可扩展性非常强!

三、应用

责任链模式的使用场景,这个就不多说了,最典型的就是 Servlet 中的 Filter,有了上面的分析,大家应该也可以理解 Servlet 中责任链模式的工作原理了,然后为什么一个一个的 Filter 需要配置在 web.xml 中,其实本质就是将 filter 注册到处理器中。

public class TestFilter implements Filter{
    public void doFilter(ServletRequest request, ServletResponse response,
            FilterChain chain) throws IOException, ServletException {
        chain.doFilter(request, response);
    }
    public void destroy() {}
    public void init(FilterConfig filterConfig) throws ServletException {}
}

四、总结

既然责任链模式这么好用,那什么时候用责任链模式?

在系统设计的时候,如果每个 if 都有一个统一的抽象,例如参数加密、系统数据验证、业务参数验证等等处理,可以将其抽象,使用对象处理进行链式调用,不仅实现优雅,而且易复用可扩展。

相关文章
|
8月前
|
设计模式 算法 Java
设计模式第十五讲:重构 - 改善既有代码的设计(下)
设计模式第十五讲:重构 - 改善既有代码的设计
252 0
|
1月前
|
设计模式 IDE Java
谈谈过度设计:因噎废食的陷阱
本文探讨了设计模式在软件开发中的应用和争议,指出设计模式虽有助于应对软件复杂性,但在互联网快速迭代的背景下,可能会导致过度设计,增加理解和修改成本。文章分析了设计模式的缺陷,如开闭原则可能导致不易修改,最小知识原则可能导致理解困难。同时,文章强调了设计模式的重要性,指出它们可以提高代码的可理解性和模块的可维护性,并提出了通过函数式设计模式进行优化的示例。作者认为,设计模式需要随着业务演进而不断演进,同时提倡使用可调试的模块和模式演进来促进系统的成长性。文章最后提醒读者,要根据实际情况选择是否使用设计模式,避免因噎废食。
162 9
|
8月前
|
设计模式 Java 测试技术
设计模式第十五讲:重构 - 改善既有代码的设计(上)
设计模式第十五讲:重构 - 改善既有代码的设计
269 0
|
设计模式 SQL Java
有点狠有点猛,我用责任链模式重构了业务代码
文章开篇,抛出一个老生常谈的问题,学习设计模式有什么作用? 设计模式主要是为了应对代码的复杂性,让其满足开闭原则,提高代码的扩展性 另外,学习的设计模式 一定要在业务代码中落实,只有理论没有真正实施,是无法真正掌握并且灵活运用设计模式的 这篇文章主要说 责任链设计模式,认识此模式是在读 Mybatis 源码时, Interceptor 拦截器主要使用的就是责任链,当时读过后就留下了很深的印象(内心 OS:还能这样玩)
|
设计模式 测试技术
重构·改善既有代码的设计.02之代码的“坏味道”
之前在《重构·改善既有代码的设计.01》中初步了解了重构的基本前提,基础原则等入门知识。今天我们继续第二更......
176 1
重构·改善既有代码的设计.02之代码的“坏味道”
|
设计模式 消息中间件 JavaScript
代码精简10倍,责任链模式yyds
代码精简10倍,责任链模式yyds
|
设计模式 机器学习/深度学习 算法
聊一聊过度设计!
新手程序员在做设计时,因为缺乏经验,很容易写出欠设计的代码,但有一些经验的程序员,尤其是在刚学习过设计模式之后,很容易写出过度设计的代码,而这种代码比新手程序员的代码更可怕,过度设计的代码不仅写出来时的成本很高,后续维护的成本也高。因为相对于毫无设计的代码,过度设计的代码有比较高的理解成本。说这么多,到底什么是过度设计?
225 0
|
设计模式 算法 Java
巧妙的运用装饰器,让你的代码高出一个逼格!
又到了周末了,阿粉祝各位网友中秋快乐,阖家团圆!同时,节日期间,阿粉不打烊,欢迎网友观看吐槽~
巧妙的运用装饰器,让你的代码高出一个逼格!
|
Java Spring
9条消除if...else的锦囊妙计,助你写出更优雅的代码(下)
9条消除if...else的锦囊妙计,助你写出更优雅的代码(下)
|
设计模式 算法 Java
9条消除if...else的锦囊妙计,助你写出更优雅的代码
9条消除if...else的锦囊妙计,助你写出更优雅的代码

相关实验场景

更多