初次写流程控制的小伙伴肯都有体会,事务处理的流程中状态一旦多了,就会出现非常多的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,如何完成一个处理任务的流程。

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

// 业务流程中的传送带
/**
* @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;
}
}

传送带建好了,现在我们需要定义该业务流程下的状态基类

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

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不断的转状态。