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