09.责任链模式

09. 责任链模式

什么是责任链设计模式?

责任链设计模式(Chain of Responsibility Pattern)是一种行为设计模式,它允许将请求沿着处理者对象组成的链进行传递,直到有一个处理者对象能够处理该请求为止。这种模式的目的是解耦请求的发送者和接收者,使得多个对象都有机会处理请求,从而增强了系统的灵活性。

责任链模式通常包含以下几个角色:

  1. 请求者(Client):发起请求的对象。
  2. 抽象处理者(Handler):定义一个处理请求的接口,通常包含一个方法用于处理请求,以及一个指向下一个处理者的引用。
  3. 具体处理者(Concrete Handler):实现抽象处理者接口的具体类,负责处理它所负责的请求,并决定是否将请求传递给链中的下一个处理者。
  4. 链(Chain):包含多个处理者对象,负责将请求沿着链传递。

责任链模式的工作原理如下:

  • 请求者创建一个请求并将其发送给链的起始处理者。

  • 每个处理者对象检查请求是否由自己处理。

    • 如果能够处理,则处理请求并结束责任链。
    • 如果不能处理,则将请求传递给链中的下一个处理者。
  • 这个过程一直持续,直到请求被处理或传递到链的末端。

责任链模式的优点包括:

  • 增强了系统的灵活性和可扩展性,因为可以动态地添加或移除处理者。
  • 降低了对象之间的耦合度,因为发送者和接收者不需要直接交互。
  • 允许多个对象处理同一个请求,增加了处理请求的灵活性。

责任链模式的缺点包括:

  • 请求的传递路径可能难以跟踪,尤其是在链很长或处理者逻辑复杂的情况下。
  • 责任链可能会导致系统性能问题,因为请求需要在多个对象之间传递。

责任链模式在实际应用中非常广泛,例如在GUI编程中处理事件、在网络编程中处理请求、在工作流系统中处理任务等场景。

举个简单的需求:

假如我们有个登录的场景,在登录处理流程中,需要校验参数、填充参数、登录判断、登录日志记录。我们每步都是环环相扣,此时就可以使用责任链模式。

有几个重要角色:

  • **Action:**责任链中的一个执行动作,主要定义具体执行动作,以及是否需要跳过。
  • **ActionChain:**责任链,用于定义添加执行动作方法,以及调度整条链路动作执行。
  • **ActionContext:**执行动作上下文,定义一个上下文对象,用来在链路执行过程中存储和传输数据。

将上面的步骤看做一个一个执行动作,建立对应的action,使用 chain 将多个 action 进行串联,同时我们可以定义一个context 上下文,用来在各个action之间传输数据。

除此之外,我们也可以通过配置中心,来定义哪些步骤需要执行,哪些可以跳过。

类图如下:

83CCDE77-5B01-46FF-882E-26C9DABFC5AB

代码编写:

1、定义顶级接口

(1)定义责任链执行动作上下文抽象类,用于责任链上下文之间数据传输。

@Data
public abstract class ActionContext implements Serializable, Cloneable {
  private static final long serialVersionUID = 1L;
  /**
   * 执行链名称,用于获取配置
   */
  private String actionChainName;

  /**
   * 跳到结果处理
   */
  private boolean isSkipToResult = false;

  public Object clone() throws CloneNotSupportedException {
     return super.clone();
  }
}

(2)定义责任链执行动作基类

public interface IAction<T extends ActionContext> {
  /**
   * 是否需要跳过
   * @param context 上下文
   * @return    true/false
   */
  default boolean isSkippered(T context) throws Exception{
    if (context.isSkipToResult()) {
      return true;
    }

    // 通过配置中心获取是否需要执行
    List<String> config = ConfigServer.getConfig(context.getActionChainName());
   if (config.contains(getName())) {
      return false;
    }
    return true;
	 }
  
   /**
   	* 执行
		* @param context 上下文
    */
  void execute(T context) throws Exception;

  /**
   * 获取执行动作名称,用于和配置中心进行匹配
   * @return
   */
  String getName();

} 

(3)定义Action 执行链接口

public interface IActionChain<T extends ActionContext> {
  /**
   * 添加一个Action
   * @param action 上下文
   * @return    action链
   */
  IActionChain<T> appendAction(Class<? extends IAction<T>> action);
  IActionChain<T> appendActions(List<Class<? extends IAction<T>>> actions);
  IActionChain<T> appendAction(IAction<T> action);

  /**
   * 执行动作
   * @param context 上下文
   */
  void execute(T context) throws Exception;
}

2、实现接口,定义具体的执行

(1)登录上下文

/**
 * 登录上下文
 */
@EqualsAndHashCode(callSuper = true)
@Data
public class LoginActionContext extends ActionContext {
  /**
   * 失败日志
   */
  private String failMsg;

  private String userName;
  private String password;
  private String token;
  private String ip;
  private String device;

  private Boolean isLoginFlag = true;
}

(2)定义责任链通用模版类

public class RouteActionChain<T extends ActionContext> implements IActionChain<T> {
  private List<IAction<T>> actionChain = new ArrayList<IAction<T>>();

  @Override
  public IActionChain<T> appendAction(Class<? extends IAction<T>> action) {
    actionChain.add(getActionInstance(action));
    return this;
  }

  @Override
  public IActionChain<T> appendActions(List<Class<? extends IAction<T>>> actions) {
    if (CollectionUtils.isEmpty(actions)) {
      return this;
    }
    for (Class<? extends IAction<T>> clazz : actions) {
      actionChain.add(getActionInstance(clazz));
    }
    return this;
  }

  @Override
  public IActionChain<T> appendAction(IAction<T> action) {
    actionChain.add(action);
    return this;
  }

  @Override
  public void execute(T context) throws Exception {
    for (IAction<T> action : actionChain) {
      //如果跳过 就不需要继续执行,这里顺序不能改变
      if (action.isSkippered(context)) {
        continue;
      }
      action.execute(context);
    }
  }

  public static <T extends ActionContext> IAction<T> getActionInstance(Class<? extends IAction<T>> clazz) {
    Collection<? extends IAction<T>> s = BeanUtil.getBeans(clazz);
    if (s != null && s.size() == 1) {
      return s.iterator().next();
    } else {
      throw new RuntimeException("action is not found");
    }
  }
}

(3)定义执行动作

  • 校验参数执行动作
  @Slf4j
  @Component
  public class CheckParamAction implements IAction<LoginActionContext> {
    @Override
    public void execute(LoginActionContext context) {
      // do something
      log.info("CheckParamAction execute......");
      // 使用断言,判断用户名不为空
      try {
        Assert.isTrue(!StringUtils.isEmpty(context.getUserName()), "用户名不能为空");
        Assert.isTrue(!StringUtils.isEmpty(context.getPassword()), "密码不能为空");
      } catch (Exception e) {
        context.setIsLoginFlag(false);
        context.setSkipToResult(true);
        context.setFailMsg(e.getMessage());
      }
    }
    
    @Override
    public String getName() {
      return "CheckParamAction";
    }
  }

填充参数执行动作

  @Slf4j
  @Component
  public class FullParamAction implements IAction<LoginActionContext> {
    @Autowired
    private ConfigServer configServer;
    @Override
    public void execute(LoginActionContext context) throws Exception {
      log.info("FullParamAction execute......");
      // 使用断言,判断用户名不为空
      try {
        // do something
        context.setIp("127.0.0.1");
        context.setDevice("PC");
        context.setToken("123456");
      } catch (Exception e) {
        context.setIsLoginFlag(false);
        context.setSkipToResult(true);
        context.setFailMsg(e.getMessage());
      }
    }
  
    @Override
    public String getName() {
      return "FullParamAction";
    }
  }
  • 登录判断执行动作
 @Slf4j
 @Component
 public class LoginAction implements IAction<LoginActionContext> {
   @Override
   public void execute(LoginActionContext context) throws Exception {
     // 模拟登录
     log.info("LoginAction execute......");
     try {
       // do something
       if(context.getUserName().equals(context.getPassword())) {
         context.setIsLoginFlag(true);
       } else {
         context.setIsLoginFlag(false);
         context.setFailMsg("用户名或密码输入错误");
       }
     } catch (Exception e) {
       context.setIsLoginFlag(false);
       context.setSkipToResult(true);
       context.setFailMsg(e.getMessage());
     }
   }
 
   @Override
   public String getName() {
     return "LoginAction";
   }
 }
  • 登录日志记录执行动作
@Slf4j
@Component
public class LogAction implements IAction<LoginActionContext> {
  @Override
  public void execute(LoginActionContext context) throws Exception {
    log.info("FullParamAction execute......");
    // 使用断言,判断用户名不为空
    try {
      // do something
      log.info("数据库插入登录日志:{}", JSONObject.toJSONString(context));
    } catch (Exception e) {
      context.setIsLoginFlag(false);
      context.setSkipToResult(true);
      context.setFailMsg(e.getMessage());
    }
  }

  @Override
  public String getName() {
    return "LogAction";
  }
}

(4)模拟配置中心

配置需要的执行动作,没有配置的自动跳过

@Component
@Data
public class ConfigServer {
  private static Map<String, List<String>> configMap = new HashMap<>();
  
  @PostConstruct
  public void init()
  {
    ArrayList<String> configList = new ArrayList<>();
    configList.add("CheckParamAction");
    configList.add("FullParamAction");
    configList.add("LogAction");
    configList.add("LoginAction");
    configMap.put("login", configList);
  }

  /**
   * 获取配置列表
   * @param actionChainName
   * @return
   */
  public static List<String> getConfig(String actionChainName) {
    return configMap.getOrDefault(actionChainName, new ArrayList<>());
  }
}

3、测试

定义测试接口

@Service
public class LoginServiceImpl implements LoginService {
  @Override
  public boolean login(String userName, String password) {
    // 创建上下文
    LoginActionContext loginActionContext = new LoginActionContext();
    loginActionContext.setActionChainName("login");
    loginActionContext.setUserName(userName);
    loginActionContext.setPassword(password);

    // 构建责任链
    RouteActionChain<LoginActionContext> chain = new RouteActionChain<>();
    chain.appendAction(CheckParamAction.class);
    chain.appendAction(FullParamAction.class);
    chain.appendAction(LoginAction.class);
    chain.appendAction(LogAction.class);
    try {
      chain.execute(loginActionContext);
    } catch (Exception e) {
      throw new RuntimeException(e);
    }
    return loginActionContext.getIsLoginFlag();
  }
}

(1)测试正常情况,传输正确的 username 和 password。

A4D6064A-3712-400A-855A-2FB75697DCBC_4_5005_c

所有执行动作正常执行。

(2)测试异常情况, 传输错误的 username 和 password。

D94B82C6-1673-48DA-9493-EED41B96234B_4_5005_c

中间 LoginAction 执行失败,自动跳出责任链,后续执行动作未执行。

到此,一个简单的责任链设计模式的 demo 就已完成。

拓展点:

​ • 可以对接配置中心,动态定义不同业务逻辑中需要执行的动作。

​ • 可以将幂等性校验,添加到判断动作是否执行逻辑中。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/671842.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

go语言linux安装

下载&#xff1a;https://go.dev/dl/ 命令行使用 wget https://dl.google.com/go/go1.19.3.linux-amd64.tar.gz解压下载的压缩包&#xff0c;linux建议放在/opt目录下 我放在/home/ihan/go_sdk下 sudo tar -C /home/ihan/go_sdk -xzf go1.19.3.linux-amd64.tar.gz 这里的参数…

STM32作业实现(一)串口通信

目录 STM32作业设计 STM32作业实现(一)串口通信 STM32作业实现(二)串口控制led STM32作业实现(三)串口控制有源蜂鸣器 STM32作业实现(四)光敏传感器 STM32作业实现(五)温湿度传感器dht11 STM32作业实现(六)闪存保存数据 STM32作业实现(七)OLED显示数据 STM32作业实现(八)触摸按…

谷歌发布文生视频模型——Veo,可生成超过一分钟高质量1080p视频

前期我们介绍过OpenAI的文生视频大模型-Sora 模型&#xff0c;其模型一经发布&#xff0c;便得到了大家疯狂的追捧。而Google最近也发布了自己的文生视频大模型Veo&#xff0c;势必要与OpenAI进行一个正面交锋。 Veo 是Google迄今为止最强大的视频生成模型。它可以生成超过一分…

学习小心意——python创建类与对象

在python中&#xff0c;类表示具有相同属性和方法的对象的集合&#xff0c;一般而言都是先定义类再创建类的实例&#xff0c;然后再通过类的实例去访问类的属性和方法 定义类 类中可以定义为数据成员和成员函数。数据成员用于描述对象特征&#xff08;相当于看人的面貌&#…

针对大模型的上下文注入攻击

大型语言模型&#xff08;LLMs&#xff09;的开发和部署取得了显著进展。例如ChatGPT和Llama-2这样的LLMs&#xff0c;利用庞大的数据集和Transformer架构&#xff0c;能够产生连贯性、上下文准确性甚至具有创造性的文本。LLMs最初和本质上是为静态场景设计的&#xff0c;即输入…

【文档智能】符合人类阅读顺序的文档模型-LayoutReader原理及权重开源

引言 阅读顺序检测旨在捕获人类读者能够自然理解的单词序列。现有的OCR引擎通常按照从上到下、从左到右的方式排列识别到的文本行&#xff0c;但这并不适用于某些文档类型&#xff0c;如多栏模板、表格等。LayoutReader模型使用seq2seq模型捕获文本和布局信息&#xff0c;用于…

libcef.dll丢失的解决方法-多种libcef.dll亲测有效解决方法分享

libcef.dll是Chromium Embedded Framework (CEF)的核心动态链接库&#xff0c;它为开发者提供了一个将Chromium浏览器嵌入到本地桌面应用程序中的解决方案。这个库使得开发者能够利用Chromium的强大功能&#xff0c;如HTML5、CSS3、JavaScript等&#xff0c;来创建跨平台的应用…

Llama(一):Mac M1芯片运行Llama3

目录 安装Ollama for Mac 下载Llama 3模型 运行Llama3 试用Llama3 在命令行中使用Llama3 背景 本地环境&#xff1a;Mac M1,16GB内存 安装Ollama for Mac 官方地址 https://ollama.com/download/Ollama-darwin.zip 链接: 百度网盘 提取码: 8wqx 下载Llama 3模型 oll…

jmeter性能优化之tomcat配置与基础调优

一、 修改tomcat初始和最大堆内存 进入到/usr/local/tomcat7-8083/bin目录下&#xff0c;编辑catalina.sh文件&#xff0c;&#xff0c;默认堆内存是600m&#xff0c;初始堆内存和最大堆内存保持一致&#xff0c; 可以更改到本机内存的70%&#xff0c;对于Linux系统&#xff0…

《平渊》· 柒 —— 大道至简?真传一句话,假传万卷书!

《平渊》 柒 "真传一句话, 假传万卷书" 对于 "大道至简"&#xff0c;不少专家可能会说出一大堆乱七八糟的名词, 比如这样&#xff1a; 所谓 "大道" 即支撑天地运转的 "系统自动力"&#xff0c;更具体地来说&#xff0c;即是天地人以…

前端Vue小兔鲜儿电商项目实战Day07

一、会员中心 - 整体功能梳理和路由配置 1. 整体功能梳理 ①个人中心 - 个人信息和猜你喜欢数据渲染②我的订单 - 各种状态下的订单列表展示 2. 路由配置&#xff08;包括三级路由配置&#xff09; ①准备个人中心模板组件 - src/views/Member/index.vue <script setup&g…

【Leetcode 705 】设计哈希集合——数组嵌套链表(限制哈希Key)

题目 不使用任何内建的哈希表库设计一个哈希集合&#xff08;HashSet&#xff09;。 实现 MyHashSet 类&#xff1a; void add(key) 向哈希集合中插入值 key 。bool contains(key) 返回哈希集合中是否存在这个值 key 。void remove(key) 将给定值 key 从哈希集合中删除。如果…

构建智慧银行保险系统的先进技术架构

随着科技的不断发展&#xff0c;智慧银行保险系统正日益受到关注。在这个数字化时代&#xff0c;构建一个先进的技术架构对于智慧银行保险系统至关重要。本文将探讨如何构建智慧银行保险系统的先进技术架构&#xff0c;以提升服务效率、降低风险并满足客户需求。 ### 1. 智慧银…

德克萨斯大学奥斯汀分校自然语言处理硕士课程汉化版(第五周) - Transformer

Transformer 1. 注意力机制 在语言建模中&#xff0c;注意力(attention)是一个关键机制&#xff0c;用于在给定上下文中访问相关信息以进行预测。注意力机制允许模型根据输入上下文中的重要信息来加权关注不同的部分&#xff0c;并根据其重要性来决定对不同部分的关注程度。 …

短视频毫无营养:四川京之华锦信息技术公司

短视频毫无营养&#xff1a;现象背后的深度剖析 在数字时代&#xff0c;短视频以其短小精悍、易于传播的特点迅速崛起&#xff0c;成为社交媒体上的热门内容。然而&#xff0c;随着短视频的泛滥&#xff0c;关于其内容质量参差不齐、缺乏营养价值的争议也日益加剧。四川京之华…

【代码随想录训练营】【Day 37】【贪心-4】| Leetcode 840, 406, 452

【代码随想录训练营】【Day 37】【贪心-4】| Leetcode 840, 406, 452 需强化知识点 python list sort的高阶用法&#xff0c;两个key&#xff0c;另一种逆序写法python list insert的用法 题目 860. 柠檬水找零 思路&#xff1a;注意 20 块找零&#xff0c;可以找3张5块升…

jpeg压缩算法学习(1)——离散余弦变换

离散余弦变换是jpeg压缩算法的关键步骤 思想 离散余弦变换的基本原理是&#xff1a;每一组离散的数据都可以由一组不同频率的余弦波来表示。 应用于图片上就是&#xff1a;将像素值转换为不同频率的余弦函数的系数&#xff08;权重&#xff09; 像素值——>权重 一维离…

52.WEB渗透测试-信息收集-CDN识别绕过(5)

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 内容参考于&#xff1a; 易锦网校会员专享课 上一个内容&#xff1a;51.WEB渗透测试-信息收集-CDN识别绕过&#xff08;4&#xff09; 端口扫描其他内容参考&…

在 GPU 上实现全规模文件系统加速

摘要 现代高性能计算和人工智能计算解决方案经常使用 GPU 作为其主要计算能力来源。这就为 GPU 应用程序的存储操作造成了严重的不平衡&#xff0c;因为每一个此类存储操作都必须向 CPU 发出信号并由 CPU 处理。在 GPU4FS 中&#xff0c;我们针对这种不平衡提出了一个彻底的解决…

11. RBAC权限管理从零到一实现(二)

前端页面已提交至git https://github.com/SJshenjian/cloud-web默认用户名密码admin 1