文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

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

2024-12-03 01:59

关注

一、介绍

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

例如,如下图:

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

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

二、示例

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

我们先编写一个加密工具类,采用AES加密算法

  1. public class AESUtil { 
  2.  
  3.     private static Logger log = LoggerFactory.getLogger(AESUtil.class); 
  4.  
  5.     private static final String AES = "AES"
  6.  
  7.     private static final String AES_CVC_PKC = "AES/CBC/PKCS7Padding"
  8.  
  9.     static { 
  10.         Security.addProvider(new BouncyCastleProvider()); 
  11.     } 
  12.  
  13.      
  14.     public static String encrypt(String content, String key) { 
  15.         try { 
  16.             SecretKeySpec secretKeySpec = new SecretKeySpec(key.getBytes(), AES); 
  17.             Cipher cipher = Cipher.getInstance(AES_CVC_PKC); 
  18.             IvParameterSpec iv = new IvParameterSpec(new byte[16]); 
  19.             cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, iv); 
  20.             byte[] encrypted = cipher.doFinal(content.getBytes()); 
  21.             return Base64.getEncoder().encodeToString(encrypted); 
  22.         }  catch (Exception e) { 
  23.             log.warn("AES加密失败,参数:{},错误信息:{}", content, ExceptionUtils.getStackTrace(e)); 
  24.             return ""
  25.         } 
  26.     } 
  27.  
  28.      
  29.     public static String decrypt(String content, String key) { 
  30.         try { 
  31.             SecretKeySpec secretKeySpec = new SecretKeySpec(key.getBytes(), AES); 
  32.             Cipher cipher = Cipher.getInstance(AES_CVC_PKC); 
  33.             IvParameterSpec iv = new IvParameterSpec(new byte[16]); 
  34.             cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, iv); 
  35.             byte[] encrypted = Base64.getDecoder().decode(content); 
  36.             byte[] original = cipher.doFinal(encrypted); 
  37.             return new String(original, "UTF-8"); 
  38.         } catch (Exception e) { 
  39.             log.warn("AES解密失败,参数:{},错误信息:{}", content, ExceptionUtils.getStackTrace(e)); 
  40.             return ""
  41.         } 
  42.     } 
  43.  
  44.  
  45.     public static void main(String[] args) throws Exception { 
  46.         String key = "1234567890123456"
  47.         String content = "{\"userCode\":\"zhangsan\",\"userPwd\":\"123456\"}"
  48.         String encryptContext = encrypt(content, "1234567890123456"); 
  49.         System.out.println("加密后的内容:" + encryptContext); 
  50.         String decryptContext = decrypt(encryptContext, key); 
  51.         System.out.println("解密后的内容:" + decryptContext); 
  52.     } 
  53.  

执行结果如下:

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

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

  1.  
  2. public class ServiceContext { 
  3.  
  4.      
  5.     private String requestParam; 
  6.  
  7.      
  8.     private String jsonData; 
  9.  
  10.      
  11.     private String userCode; 
  12.  
  13.      
  14.     private String userPwd; 
  15.  
  16.     //省略set\get 
  17.  
  18.     public ServiceContext() { 
  19.     } 
  20.  
  21.     public ServiceContext(String requestParam) { 
  22.         this.requestParam = requestParam; 
  23.     } 
  1. public interface HandleIntercept { 
  2.  
  3.      
  4.     ServiceContext handle(ServiceContext context); 
  5.  
  1.  
  2. public class DecodeDataHandle implements HandleIntercept { 
  3.  
  4.     private String key = "1234567890123456"
  5.  
  6.     @Override 
  7.     public ServiceContext handle(ServiceContext context) { 
  8.         String jsonData = AESUtil.decrypt(context.getRequestParam(), key); 
  9.         if(StringUtils.isEmpty(jsonData)){ 
  10.             throw new IllegalArgumentException("解密失败"); 
  11.         } 
  12.         context.setJsonData(jsonData); 
  13.         return context; 
  14.     } 
  1.  
  2. public class ValidDataHandle implements HandleIntercept { 
  3.  
  4.     @Override 
  5.     public ServiceContext handle(ServiceContext context) { 
  6.         String jsonData = context.getJsonData(); 
  7.         JSONObject jsonObject = JSONObject.parseObject(jsonData); 
  8.         if(!jsonObject.containsKey("userCode")){ 
  9.             throw new IllegalArgumentException("userCode不能为空"); 
  10.         } 
  11.         context.setUserCode(jsonObject.getString("userCode")); 
  12.         if(!jsonObject.containsKey("userPwd")){ 
  13.             throw new IllegalArgumentException("userPwd不能为空"); 
  14.         } 
  15.         context.setUserPwd(jsonObject.getString("userPwd")); 
  16.         return context; 
  17.     } 

最后创建一个处理链路管理器HandleChain

  1.  
  2. public class HandleChain { 
  3.      
  4.     private List handleInterceptList = new ArrayList<>(); 
  5.  
  6.      
  7.     public void addHandle(HandleIntercept handleIntercept){ 
  8.         handleInterceptList.add(handleIntercept); 
  9.     } 
  10.  
  11.      
  12.     public ServiceContext execute(ServiceContext context){ 
  13.         if(!handleInterceptList.isEmpty()){ 
  14.             for (HandleIntercept handleIntercept : handleInterceptList) { 
  15.                 context =handleIntercept.handle(context); 
  16.             } 
  17.         } 
  18.         return context; 
  19.     } 

写完之后,我们编写一个测试类ChainClientTest

  1. public class ChainClientTest { 
  2.  
  3.     public static void main(String[] args) { 
  4.         //获取请求参数 
  5.         String requestParam = "5ELORDsYKxCz6Ec377udct7dBMI74ZtJDCFL4B3cpoBsPC8ILH/aiaRFnZa/oTC5"
  6.         //封装请求参数 
  7.         ServiceContext serviceContext = new ServiceContext(requestParam); 
  8.  
  9.         //添加处理链路 
  10.         HandleChain handleChain = new HandleChain(); 
  11.         handleChain.addHandle(new DecodeDataHandle());//解密处理 
  12.         handleChain.addHandle(new ValidDataHandle());//数据验证处理 
  13.  
  14.         //执行处理链,获取处理结果 
  15.         serviceContext = handleChain.execute(serviceContext); 
  16.         System.out.println("处理结果:" + JSONObject.toJSONString(serviceContext)); 
  17.     } 

执行之后结果如下:

  1. 处理结果:{"jsonData":"{\"userCode\":\"zhangsan\",\"userPwd\":\"123456\"}","requestParam":"5ELORDsYKxCz6Ec377udct7dBMI74ZtJDCFL4B3cpoBsPC8ILH/aiaRFnZa/oTC5","userCode":"zhangsan","userPwd":"123456"

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

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

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

  1. if(condition){ 
  2.     if(condition){ 
  3.         if(condition){ 
  4.    //业务处理 
  5.         } 
  6.     } 

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

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

三、应用

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

  1. public class TestFilter implements Filter{ 
  2.  
  3.     public void doFilter(ServletRequest request, ServletResponse response, 
  4.             FilterChain chain) throws IOException, ServletException { 
  5.         chain.doFilter(request, response); 
  6.     } 
  7.  
  8.     public void destroy() {} 
  9.     public void init(FilterConfig filterConfig) throws ServletException {} 

四、总结

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

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

五、参考

 

五月的仓颉 - 责任链模式

 

来源: Java极客技术内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯