如何做好多状态的流程控制

初次写流程控制的小伙伴肯都有体会,事务处理的流程中状态一旦多了,就会出现非常多的if-elseif 语句,对于出现Bug解决问题来说,不得不对整段代码的逻辑理一遍,然后才能分析出到底是哪里出了问题。那么,有什么别的好的解决方案吗?

在设计模式中,其实早就已经为我们考虑到了这一个场景,我们可以利用状态模式基本还原这样的任务。

在这里插入图片描述

整个实现上的核心基础类就是 基础状态抽象类 和基础有限自动机。业务状态标识了业务流程中的一个个节点,而有限状态机是业务流程的运输体。想象一下,把汽车生产流程当做一个办事流程,那么有限自动机就是传送带,状态就是一道道工序。

// State.java
/**
 * @auther chaojilaji
 * @version 1.0
 * <pre>状态基础</pre>
 * <p>主要定义了基础的状态的进入,状态的释放等功能</p>
 */
public abstract class State {
    public State() {
    }
    /**
     * 将当前状态加入到某个状态机中
     * @param var1 有限状态机
     */
    public abstract void enter(FiniteStateMachine var1);
    /**
     * 从某个状态机中退出
     * @param var1 有限状态机
     */
    public abstract void exit(FiniteStateMachine var1);
    public abstract void pause(FiniteStateMachine var1);
    public abstract void resume(FiniteStateMachine var1);
}

基础状态类只需要定义出所有状态的基础功能即可。即 进入传送带、从传送带上面取出来等。
下面来定义基础传动带

// FiniteStateMachine.java
/**
 * @author chaojilaji
 * @version 1.0
 * <pre>有限状态机可以看做是处理事务流程的载体</pre>
 */
public class FiniteStateMachine{


    protected EmptyState nullState = new EmptyState();
    private Stack<State> states = new Stack();

    /**
     *
     * @return 获取当前状态机中的状态
     */
    public State getCurrentState() {
        return (State)(this.states.size() == 0 ? this.nullState : (State)this.states.peek());
    }

    public FiniteStateMachine() {
    }

    /**
     * 状态切换
     * @param newSate 进入到新状态
     */
    public void ChangeTo(State newSate) {
        this.getCurrentState().exit(this);
        if (this.states.size() > 0) {
            this.states.pop();
        }

        this.states.push(newSate);
        System.out.println("状态----------"+newSate.getClass());
        this.getCurrentState().enter(this);
    }
}

// EmptyState.java
public class EmptyState extends State{

    public EmptyState() {
    }
    @Override
    public void enter(FiniteStateMachine var1) {

    }

    @Override
    public void exit(FiniteStateMachine var1) {

    }

    @Override
    public void pause(FiniteStateMachine var1) {

    }

    @Override
    public void resume(FiniteStateMachine var1) {

    }
}

好了,现在第一层基础模块已经写好了,现在我们来实现一个流程demo,如何完成一个处理任务的流程。
在这里插入图片描述

// 业务流程中的传送带
/**
 * @author chaojilaji
 * @version 1.0
 * <pre>扫描任务领域有限状态机</pre>
 */
public class TaskFiledMachine extends FiniteStateMachine implements TaskThingListener{

    private Long taskId;


    public TaskFiledMachine(Long taskId){
        this.taskId = taskId;
        nullState = new EmptyState();
        ChangeTo(new NotBeginState());
    }


    @Override
    public void onCommand(OperateCode operateCode) {
        try {
            ((TaskState)getCurrentState()).onCommand(operateCode);
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}

// TaskThingListener 接口定义了传送带在这个业务领域中要干的事儿
public interface TaskThingListener {

    void onCommand(OperateCode operateCode);

}

// OperateCode 是操作码
public class OperateCode{

    private Integer code;
    private Map<String,Object> body;

    public OperateCode(Integer code){
        this.code = code;
    }

    public void setBody(Map<String, Object> body) {
        this.body = body;
    }

    public Integer getCode() {
        return code;
    }

    public Map<String, Object> getBody() {
        return body;
    }
}

传送带建好了,现在我们需要定义该业务流程下的状态基类
在这里插入图片描述

public abstract class TaskState extends State {

    /**
     * 指定当前业务状态所属的业务状态机
     */
    protected TaskFiledMachine taskFiledMachine;


    public abstract void onCommand(OperateCode operateCode);


    @Override
    public void enter(FiniteStateMachine var1) {
        taskFiledMachine = (TaskFiledMachine) var1;
    }

    @Override
    public void exit(FiniteStateMachine var1) {
        taskFiledMachine = null;
    }

    @Override
    public void pause(FiniteStateMachine var1) {

    }

    @Override
    public void resume(FiniteStateMachine var1) {

    }
}

然后我们只需要基于这个业务场景,通过继承这个TaskState ,定义出一道道工序(状态)

// 未开始的状态
/**
 * @author chaojilaji
 * @version 1.0
 * <pre>未开始的状态</pre>
 */
@Slf4j
public class NotBeginState extends TaskState {

    @Override
    public void onCommand(OperateCode operateCode) {
        if (operateCode.getCode().equals(OperateCodeEnum.NEW_TASK.getCode())){
            log.info("新建任务了");
            taskFiledMachine.ChangeTo(new QueueState());
        }
    }
}
// 队列中状态
/**
 * @author chaojilaji
 * @version 1.0
 * <pre>队列状态</pre>
 */
@Slf4j
public class QueueState extends TaskState {

    @Override
    public void onCommand(OperateCode operateCode){
        if (operateCode.getCode().equals(OperateCodeEnum.INIT_STATE.getCode())){
            log.info("开始做---出队列---后要做的事儿,先检测参数完整性,再做一些处理");
            if (operateCode.getBody().containsKey("task_id")){
                log.info("taskId {},进入到队列中处理",operateCode.getBody().get("task_id"));
            }
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            log.info("taskId {},队列中处理完毕,转入到扫描中状态",operateCode.getBody().get("taskId"));
            taskFiledMachine.ChangeTo(new ScanningState());
            taskFiledMachine.onCommand(operateCode);
        }
    }
}

// 任务执行中状态
/**
 * @author chaojilaji
 * @version 1.0
 * <pre>扫描中</pre>
 */
@Slf4j
public class ScanningState extends TaskState {

    @Override
    public void onCommand(OperateCode operateCode) {
        if (operateCode.getCode().equals(OperateCodeEnum.INIT_STATE.getCode())){
            log.info("开始做---扫描中---要做的事儿,先检测参数完整性,再做一些处理");
            if (operateCode.getBody().containsKey("task_id")){
                log.info("taskId {},进入到扫描中处理",operateCode.getBody().get("task_id"));
            }
            try {
                Thread.sleep(1000*1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            log.info("taskId {},扫描中处理完毕,转入到扫描完毕状态",operateCode.getBody().get("taskId"));
            taskFiledMachine.ChangeTo(new ScanEndState());
            taskFiledMachine.onCommand(operateCode);
        }
    }
}

// 任务执行完毕状态
/**
 * @author chaojilaji
 * @version 1.0
 * <pre>扫描完成状态</pre>
 */
@Slf4j
public class ScanEndState extends TaskState {

    @Override
    public void onCommand(OperateCode operateCode) {
        if (operateCode.getCode().equals(OperateCodeEnum.INIT_STATE.getCode())){
            log.info("开始做---扫描完毕---要做的事儿,先检测参数完整性,再做一些处理");
            if (operateCode.getBody().containsKey("task_id")){
                log.info("taskId {},进入到扫描完毕处理",operateCode.getBody().get("task_id"));
            }
            try {
                Thread.sleep(1000*1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            log.info("taskId {},扫描完毕处理完毕,转入到扫描完毕状态",operateCode.getBody().get("taskId"));
        }
    }
}

最后来看看如何触发这个流程

// 定义一个创建任务的接口,初始化流程
@Autowired
private ScanTask scanTask;

@PostMapping("/change/state")
    @ResponseBody
    public Object changeStatus(@RequestParam("task_id")final Long taskId,
                               @RequestParam("operate_code")final Integer operateCode){
        Map<String,Object> ans = new HashMap<>();
        ans.put("code",400);
        if (Objects.isNull(taskId)){
            return ans;
        }
        TaskFiledMachine taskFiledMachine = null;
        if (scanTask.taskFiledMachineConcurrentHashMap.containsKey(taskId)){
           taskFiledMachine = scanTask.taskFiledMachineConcurrentHashMap.get(taskId);
           if (Objects.isNull(taskFiledMachine)){
               return ans;
           }

        }else {
            taskFiledMachine = new TaskFiledMachine(taskId);
        }
        try {
            taskFiledMachine.onCommand(new OperateCode(operateCode));
            scanTask.taskFiledMachineConcurrentHashMap.put(taskId,taskFiledMachine);
            ans.put("code",200);
        }catch (Exception e){
            e.printStackTrace();
        }
        return ans;
    }
    
// 流程化任务服务
@Service
public class ScanTask implements InitializingBean {

    private PriorityBlockingQueue<Long> tasks = new PriorityBlockingQueue<>(16);
    public Map<Long, TaskFiledMachine> taskFiledMachineConcurrentHashMap = new ConcurrentHashMap<>();

    private void dispatch() throws InterruptedException {
        while (true){
            Long taskId = tasks.take();
            TaskFiledMachine taskFiledMachine = taskFiledMachineConcurrentHashMap.get(taskId);
            taskFiledMachine.onCommand(new OperateCode(OperateCodeEnum.INIT_STATE.getCode()));
        }
    }


    @Override
    public void afterPropertiesSet() throws Exception {
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    while (true){

                    }
                }catch (Exception e){
                    e.printStackTrace();
                }

            }
        }).start();
    }
}

简单来说,通过onCommand的调用进入到状态处理,状态间通过changeTo不断的转状态。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章