在网上做翻译的网站,外贸网站如何做,网站建设柚子网络科技官网,wordpress 白色09. 责任链模式
什么是责任链设计模式#xff1f;
责任链设计模式#xff08;Chain of Responsibility Pattern#xff09;是一种行为设计模式#xff0c;它允许将请求沿着处理者对象组成的链进行传递#xff0c;直到有一个处理者对象能够处理该请求为止。这种模式的目的…09. 责任链模式
什么是责任链设计模式
责任链设计模式Chain of Responsibility Pattern是一种行为设计模式它允许将请求沿着处理者对象组成的链进行传递直到有一个处理者对象能够处理该请求为止。这种模式的目的是解耦请求的发送者和接收者使得多个对象都有机会处理请求从而增强了系统的灵活性。
责任链模式通常包含以下几个角色
请求者Client发起请求的对象。抽象处理者Handler定义一个处理请求的接口通常包含一个方法用于处理请求以及一个指向下一个处理者的引用。具体处理者Concrete Handler实现抽象处理者接口的具体类负责处理它所负责的请求并决定是否将请求传递给链中的下一个处理者。链Chain包含多个处理者对象负责将请求沿着链传递。
责任链模式的工作原理如下 请求者创建一个请求并将其发送给链的起始处理者。 每个处理者对象检查请求是否由自己处理。 如果能够处理则处理请求并结束责任链。如果不能处理则将请求传递给链中的下一个处理者。 这个过程一直持续直到请求被处理或传递到链的末端。
责任链模式的优点包括
增强了系统的灵活性和可扩展性因为可以动态地添加或移除处理者。降低了对象之间的耦合度因为发送者和接收者不需要直接交互。允许多个对象处理同一个请求增加了处理请求的灵活性。
责任链模式的缺点包括
请求的传递路径可能难以跟踪尤其是在链很长或处理者逻辑复杂的情况下。责任链可能会导致系统性能问题因为请求需要在多个对象之间传递。
责任链模式在实际应用中非常广泛例如在GUI编程中处理事件、在网络编程中处理请求、在工作流系统中处理任务等场景。
举个简单的需求
假如我们有个登录的场景在登录处理流程中需要校验参数、填充参数、登录判断、登录日志记录。我们每步都是环环相扣此时就可以使用责任链模式。
有几个重要角色
**Action**责任链中的一个执行动作主要定义具体执行动作以及是否需要跳过。**ActionChain**责任链用于定义添加执行动作方法以及调度整条链路动作执行。**ActionContext**执行动作上下文定义一个上下文对象用来在链路执行过程中存储和传输数据。
将上面的步骤看做一个一个执行动作建立对应的action使用 chain 将多个 action 进行串联同时我们可以定义一个context 上下文用来在各个action之间传输数据。
除此之外我们也可以通过配置中心来定义哪些步骤需要执行哪些可以跳过。
类图如下 代码编写
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 IActionT extends ActionContext {/*** 是否需要跳过* param context 上下文* return true/false*/default boolean isSkippered(T context) throws Exception{if (context.isSkipToResult()) {return true;}// 通过配置中心获取是否需要执行ListString 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 IActionChainT extends ActionContext {/*** 添加一个Action* param action 上下文* return action链*/IActionChainT appendAction(Class? extends IActionT action);IActionChainT appendActions(ListClass? extends IActionT actions);IActionChainT appendAction(IActionT 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 RouteActionChainT extends ActionContext implements IActionChainT {private ListIActionT actionChain new ArrayListIActionT();Overridepublic IActionChainT appendAction(Class? extends IActionT action) {actionChain.add(getActionInstance(action));return this;}Overridepublic IActionChainT appendActions(ListClass? extends IActionT actions) {if (CollectionUtils.isEmpty(actions)) {return this;}for (Class? extends IActionT clazz : actions) {actionChain.add(getActionInstance(clazz));}return this;}Overridepublic IActionChainT appendAction(IActionT action) {actionChain.add(action);return this;}Overridepublic void execute(T context) throws Exception {for (IActionT action : actionChain) {//如果跳过 就不需要继续执行这里顺序不能改变if (action.isSkippered(context)) {continue;}action.execute(context);}}public static T extends ActionContext IActionT getActionInstance(Class? extends IActionT clazz) {Collection? extends IActionT s BeanUtil.getBeans(clazz);if (s ! null s.size() 1) {return s.iterator().next();} else {throw new RuntimeException(action is not found);}}
}3定义执行动作
校验参数执行动作 Slf4jComponentpublic class CheckParamAction implements IActionLoginActionContext {Overridepublic void execute(LoginActionContext context) {// do somethinglog.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());}}Overridepublic String getName() {return CheckParamAction;}}填充参数执行动作 Slf4jComponentpublic class FullParamAction implements IActionLoginActionContext {Autowiredprivate ConfigServer configServer;Overridepublic void execute(LoginActionContext context) throws Exception {log.info(FullParamAction execute......);// 使用断言判断用户名不为空try {// do somethingcontext.setIp(127.0.0.1);context.setDevice(PC);context.setToken(123456);} catch (Exception e) {context.setIsLoginFlag(false);context.setSkipToResult(true);context.setFailMsg(e.getMessage());}}Overridepublic String getName() {return FullParamAction;}}登录判断执行动作 Slf4jComponentpublic class LoginAction implements IActionLoginActionContext {Overridepublic void execute(LoginActionContext context) throws Exception {// 模拟登录log.info(LoginAction execute......);try {// do somethingif(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());}}Overridepublic String getName() {return LoginAction;}}登录日志记录执行动作
Slf4j
Component
public class LogAction implements IActionLoginActionContext {Overridepublic void execute(LoginActionContext context) throws Exception {log.info(FullParamAction execute......);// 使用断言判断用户名不为空try {// do somethinglog.info(数据库插入登录日志:{}, JSONObject.toJSONString(context));} catch (Exception e) {context.setIsLoginFlag(false);context.setSkipToResult(true);context.setFailMsg(e.getMessage());}}Overridepublic String getName() {return LogAction;}
}4模拟配置中心
配置需要的执行动作没有配置的自动跳过
Component
Data
public class ConfigServer {private static MapString, ListString configMap new HashMap();PostConstructpublic void init(){ArrayListString configList new ArrayList();configList.add(CheckParamAction);configList.add(FullParamAction);configList.add(LogAction);configList.add(LoginAction);configMap.put(login, configList);}/*** 获取配置列表* param actionChainName* return*/public static ListString getConfig(String actionChainName) {return configMap.getOrDefault(actionChainName, new ArrayList());}
}3、测试
定义测试接口
Service
public class LoginServiceImpl implements LoginService {Overridepublic boolean login(String userName, String password) {// 创建上下文LoginActionContext loginActionContext new LoginActionContext();loginActionContext.setActionChainName(login);loginActionContext.setUserName(userName);loginActionContext.setPassword(password);// 构建责任链RouteActionChainLoginActionContext 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。 所有执行动作正常执行。
2测试异常情况 传输错误的 username 和 password。 中间 LoginAction 执行失败自动跳出责任链后续执行动作未执行。
到此一个简单的责任链设计模式的 demo 就已完成。
拓展点
• 可以对接配置中心动态定义不同业务逻辑中需要执行的动作。
• 可以将幂等性校验添加到判断动作是否执行逻辑中。