Apache Commons Chain 实现命令模式

242 阅读3分钟

一. 前言

1. 概述

实现命令模式的方式有很多, 本章使用Apache Commons Chain 实现命令模式。

2. 命令模式简介

命令模式是一种行为型的设计模式。它聚焦于请求数据, 把对请求数据的操作封装成一组命令对象。 由客户端直接发起对调用者的链路调用, 调用者完成对具体命令对象的编排(组织命令对象的执行顺序) 及其执行.

3. 命令模式适用场景

  • 自动化流程处理: 客户端发起请求,即可由调用者组织命令对象完成一系列的步骤
  • 多线程流式处理: 可以处理多个的任务的并发执行 (多个命令对象的并发执行任务)

对于一般固定流程的执行, 可以不用命令模式.

命令模式的优势体现在经常需要变更流程的场景:

a. 需要在多个命令的执行过程中, 新增命令对象 b. 需要重新编排命令对象的执行顺序

二. 使用步骤

1. 引入依赖

<dependency>
    <groupId>commons-chain</groupId>
    <artifactId>commons-chain</artifactId>
    <version>1.2</version>
</dependency>

2. 定义命令对象

命令对象通过实现Command接口及其对应的execute方法完成逻辑处理。 execute方法返回Boolean类型, 可通过Command接口的静态变量返回。 CONTINUE_PROCESSING 表示继续执行下一个命令对象。 PROCESSING_COMPLETE 表示终止流程执行。

a. 校验登陆的命令对象

public class ValidateLoginCommand implements Command {
    @Override
    public boolean execute(Context context) throws Exception {
        String loginName = (String) context.get("loginName");
        System.out.println("执行了校验登陆的执行方法");
        if (StringUtils.isNotEmpty(loginName)) {
            return Command.CONTINUE_PROCESSING;
        }
        return Command.PROCESSING_COMPLETE;
    }
}

b. 执行登陆的命令对象

public class ExecuteLoginCommand implements Command {
    
    @Override
    public boolean execute(Context context) throws Exception {
        String password = (String) context.get("password");
        System.out.println("执行了登陆命令的执行方法");
        if (StringUtils.isEmpty(password)) {
            return Command.PROCESSING_COMPLETE;
        }
        return Command.CONTINUE_PROCESSING;
    }
}

3. 定义调用链对象

调用链对象通过继承ChainBase对象, 通过ChainBase对象的addCommand方法完成命令的添加

public class LoginChain extends ChainBase {

    public LoginChain(){
        super();
        //添加命令对象的过滤器
        addCommand(new LoginCommandFilter());
        addCommand(new ValidateLoginCommand());
        addCommand(new ExecuteLoginCommand());
    }
}

4. 执行调用链的execute方法

execute方法的参数contexth实际就是各个命令对象使用的数据上下文。 类似LiteFlow框架的DefaultContext (工作台设计思想)

@RestController
@RequestMapping("/loginChain")
public class LoginChainController {

    @GetMapping("/test")
    public void test() throws Exception {
        LoginChain loginChain = new LoginChain();
        Context context = new ContextBase();
        context.put("password","123");
        loginChain.execute(context);
    }
}

5. 扩展-命令对象的增强

有时候, 我们可能需要在命令对象执行前完成一些初始化步骤, 比如日志打印或者数据初始化, 在整个流程结束后完成资源的释放等动作。可以使用命令对象拦截器完成这个处理。 execute方法可以在命令对象的execute方法执行前先执行, 在所有命令对象执行完成后, 再执行对应的postprocess方法。该拦截器也需要添加到对应的调用链中 (见3中的代码注释: 添加命令对象的过滤器)

public class LoginCommandFilter implements Filter {

    @Override
    public boolean postprocess(Context context, Exception exception) {
        System.out.println("执行了登陆命令过滤器的后置方法");
        return false;
    }

    @Override
    public boolean execute(Context context) throws Exception {
        System.out.println("执行了登陆命令过滤器的执行方法");
        return false;
    }
}