天天看點

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

初次寫流程控制的小夥伴肯都有體會,事務處理的流程中狀态一旦多了,就會出現非常多的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();
    }
}