刚换了一家公司,学习了公司内部的项目,带我的师兄让我学习下公司项目中状态机的实现,并自己实现一个状态机。以此来记录学习状态机的过程。
https://blog.csdn.net/xinghuanmeiying/article/details/81586954
通过这篇博文学习了状态机图如何绘制,以及状态机图中的一些概念,不清楚的状态机的小伙伴可以学习下。(不知道这算不算侵权哈,反正自己也是通过别人的博客学习的)。
博客的末尾处一张图很有意思。
简洁明了的解释了工作中某岗位的爱恨情仇啊,接下来就使用java语言简单实现这张状态机图吧。
先说一下实现的具体思路。
1.状态机图由各种状态(state)以及操作(operation)组成。
2.每个状态都是通过某种操作达到另一种状态。
3.熟悉设计模式的小伙伴可能会联想到责任链设计模式,单个对象通过维护一个next对象,来达到节点状态的扭转。
具体实现:
说在前面的话,状态机的代码具体实现有很多种,其中包括大名鼎鼎的spring也对状态机进行了实现(spring statemachine),状态机适用于多种业务线以及每条业务线存在很多状态的场景,使用状态,操作维护了某一个业务流程,是的复杂繁琐的流程把控变得简洁起来。我这里实现的思路是通过一个xml文件,定义一些操作,状态。在每个状态下可以执行哪些操作,以及执行完之后所对应的状态来实现状态机的。
<?xml version="1.0" encoding="UTF-8"?>
<state-action xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="state-action-1.2.xsd">
<operations id="beikaifada" name="被开发打" >
<operation name="beikaifada" i18n="被开发打" url="/beikaifada"/>
</operations>
<operations id="yangshangchenggong" name="养伤成功" >
<operation name="yangshangchenggong" i18n="养伤成功" url="/yangshangchenggong"/>
</operations>
<operations id="yangshangshibai" name="养伤失败" >
<operation name="yangshangshibai" i18n="养伤失败" url="/yangshangshibai"/>
</operations>
<operations id="lingdaobumanyiduini" name="领导不满意怼你" >
<operation name="lingdaobumanyiduini" i18n="领导不满意怼你" url="/lingdaobumanyiduini"/>
</operations>
<operations id="yangshangchenggonggaixuqiuchenggong" name="养伤成功改需求成功" >
<operation name="yangshangchenggonggaixuqiuchenggong" i18n="养伤成功改需求成功" url="/yangshangchenggonggaixuqiuchenggong"/>
</operations>
<operations id="gaixuqiuchenggong" name="修改需求成功" >
<operation name="gaixuqiuchenggong" i18n="修改需求成功" url="/gaixuqiuchenggong"/>
</operations>
<operations id="lingdaobumanyi" name="领导不满意" >
<operation name="lingdaobumanyi" i18n="领导不满意" url="/lingdaobumanyi"/>
</operations>
<operations id="gaixuqiu" name="改需求" >
<operation name="gaixuqiu" i18n="改需求" url="/gaixuqiu"/>
</operations>
<operations id="siwang" name="死亡" >
<operation name="siwang" i18n="改需求" url="/siwang"/>
</operations>
<!-- 状态机主要流程 -->
<states statusFiledName="status">
<state id="startstate" name="startstate" i18n="开始">
<operations ref="gaixuqiu" to="gaixuqiustate"/>
</state>
<state id="gaixuqiustate" name="gaixuqiustate" i18n="改需求">
<operations ref="beikaifada" id="beikaifada" to="yangshangstate"/>
<operations ref="gaixuqiuchenggong" id="gaixuqiuchenggong" to="zuohuibaostate"/>
</state>
<state id="yangshangstate" name="yangshangstate" i18n="养伤">
<operations ref="yangshangshibai" id="yangshangshibai" to="siwangstate"/>
<operations ref="yangshangchenggong" id="yangshangchenggong" to="gaixuqiustate"/>
<operations ref="yangshangchenggonggaixuqiuchenggong" id="yangshangchenggonggaixuqiuchenggong" to="zuohuibaostate"/>
</state>
<state id="zuohuibaostate" name="zuohuibaostate" i18n="做汇报">
<operations ref="lingdaobumanyi" id="lingdaobumanyi" to="gaixuqiustate"/>
<operations ref="lingdaobumanyiduini" id="lingdaobumanyiduini" to="yangshangstate"/>
</state>
<state id="siwangstate" name="siwangstate" i18n="死亡">
<operations ref="siwang" id="siwang" to="siwangstate"/>
</state>
</states>
</state-action>
根据那张爱恨情仇图片定义了一些具体操作,每个操作可以拥有一些标志,比如某操作执行对应到项目中某个方法,都可以在这边定义。
<operations id="beikaifada" name="被开发打" >
<operation name="beikaifada" i18n="被开发打" url="/beikaifada"/>
</operations>
<operations id="yangshangchenggong" name="养伤成功" >
<operation name="yangshangchenggong" i18n="养伤成功" url="/yangshangchenggong"/>
</operations>
<operations id="yangshangshibai" name="养伤失败" >
<operation name="yangshangshibai" i18n="养伤失败" url="/yangshangshibai"/>
</operations>
<operations id="lingdaobumanyiduini" name="领导不满意怼你" >
<operation name="lingdaobumanyiduini" i18n="领导不满意怼你" url="/lingdaobumanyiduini"/>
</operations>
<operations id="yangshangchenggonggaixuqiuchenggong" name="养伤成功改需求成功" >
<operation name="yangshangchenggonggaixuqiuchenggong" i18n="养伤成功改需求成功" url="/yangshangchenggonggaixuqiuchenggong"/>
</operations>
<operations id="gaixuqiuchenggong" name="修改需求成功" >
<operation name="gaixuqiuchenggong" i18n="修改需求成功" url="/gaixuqiuchenggong"/>
</operations>
<operations id="lingdaobumanyi" name="领导不满意" >
<operation name="lingdaobumanyi" i18n="领导不满意" url="/lingdaobumanyi"/>
</operations>
<operations id="gaixuqiu" name="改需求" >
<operation name="gaixuqiu" i18n="改需求" url="/gaixuqiu"/>
</operations>
<operations id="siwang" name="死亡" >
<operation name="siwang" i18n="改需求" url="/siwang"/>
</operations>
接下来就是在状态中维护这些操作,我们参考状态机图,定义如下:
<!-- 状态机主要流程 -->
<states statusFiledName="status">
<state id="startstate" name="startstate" i18n="开始">
<operations ref="gaixuqiu" to="gaixuqiustate"/>
</state>
<state id="gaixuqiustate" name="gaixuqiustate" i18n="改需求">
<operations ref="beikaifada" id="beikaifada" to="yangshangstate"/>
<operations ref="gaixuqiuchenggong" id="gaixuqiuchenggong" to="zuohuibaostate"/>
</state>
<state id="yangshangstate" name="yangshangstate" i18n="养伤">
<operations ref="yangshangshibai" id="yangshangshibai" to="siwangstate"/>
<operations ref="yangshangchenggong" id="yangshangchenggong" to="gaixuqiustate"/>
<operations ref="yangshangchenggonggaixuqiuchenggong" id="yangshangchenggonggaixuqiuchenggong" to="zuohuibaostate"/>
</state>
<state id="zuohuibaostate" name="zuohuibaostate" i18n="做汇报">
<operations ref="lingdaobumanyi" id="lingdaobumanyi" to="gaixuqiustate"/>
<operations ref="lingdaobumanyiduini" id="lingdaobumanyiduini" to="yangshangstate"/>
</state>
<state id="siwangstate" name="siwangstate" i18n="死亡">
<operations ref="siwang" id="siwang" to="siwangstate"/>
</state>
</states>
这里面对每一个状态执行的动作,以及执行完之后对应的状态都进行了定义,这样使得繁琐的状态机图一目了然。
接下来的实现就很简单了,对xml进行解析,维护成一个对象,放在内存中。
public Map<String,List<OperationDto>> init(){
Map<String,List<OperationDto>> dataSource=new HashMap<>();
try {
File file = new File("/Users/fengyue/IdeaProjects/state_machine_demo/src/main/resources/lassen_suit.state-action.xml");
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document doc = builder.parse(file);
NodeList state = doc.getElementsByTagName("state");
for (int i = 0; i < state.getLength(); i++) {
List<OperationDto> operations = new ArrayList<>();
Node node = state.item(i);
NamedNodeMap nodeMap = node.getAttributes();
String stateStr = nodeMap.getNamedItem("id").getNodeValue();
//------------------开始解析operation--------------------
NodeList childNodes = ((DeferredElementImpl)node).getElementsByTagName("operations");
for (int j = 0; j < childNodes.getLength(); j++) {
OperationDto dto=new OperationDto();
Node item = childNodes.item(j);
String operationCode = item.getAttributes().getNamedItem("ref").getNodeValue();
String to = item.getAttributes().getNamedItem("to").getNodeValue();
dto.setCode(operationCode);
dto.setTo(to);
operations.add(dto);
}
dataSource.put(stateStr,operations);
}
} catch (Exception e) {
e.printStackTrace();
}
return dataSource;
}
因为只是简单对demo,所以每个操作都做成了一个请求,写在了controller中。
package com.example.state_machine_demo.Controller;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import com.example.state_machine_demo.dto.ActionState;
import com.example.state_machine_demo.dto.OperationDto;
import com.example.state_machine_demo.util.Index;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.ModelAndView;
@Controller
public class IndexController {
public static final Map<String,String> stateMap=new HashMap<String,String>();
public static final Map<String,String> operationMap=new HashMap<String,String>();
static {
stateMap.put("startstate","开始");
stateMap.put("gaixuqiustate","改需求");
stateMap.put("yangshangstate","养伤");
stateMap.put("zuohuibaostate","做汇报");
stateMap.put("siwangstate","死亡");
operationMap.put("beikaifada","被开发打");
operationMap.put("yangshangchenggong","养伤成功");
operationMap.put("yangshangshibai","养伤失败");
operationMap.put("lingdaobumanyiduini","领导不满意怼你");
operationMap.put("yangshangchenggonggaixuqiuchenggong","养伤成功改需求成功");
operationMap.put("gaixuqiuchenggong","修改需求成功");
operationMap.put("lingdaobumanyi","领导不满意");
operationMap.put("gaixuqiu","改需求");
operationMap.put("siwang","死亡");
}
@Autowired
private Index util;
@GetMapping("/index")
public ModelAndView index(@RequestParam String operation) {
ArrayList<OperationDto> dtos = new ArrayList<>();
ModelAndView view = new ModelAndView();
view.setViewName("index");
Map<String, List<OperationDto>> dataSource = util.init();
List<OperationDto> list = dataSource.get("startstate");
for (int i = 0; i < list.size(); i++) {
OperationDto operationDto = list.get(i);
operationDto.setName(operationMap.get(list.get(i).getCode()));
operationDto.setUrl("http://localhost:8080/"+list.get(i).getCode()+"?operation="+list.get(i).getCode()+"&state="+operationDto.getTo());
dtos.add(operationDto);
}
List<OperationDto> dtoList = list.stream().filter(e -> operation.equals(e.getCode())).collect(
Collectors.toList());
if(null == dtoList || dtoList.size()==0){
return view;
}
view.addObject("state",stateMap.get("startstate"));
view.addObject("operationList",dtos);
return view;
}
@GetMapping("/beikaifada")
public ModelAndView beikaifada(@RequestParam String operation,@RequestParam String state) {
System.out.println("被开发打...");
ArrayList<OperationDto> dtos = new ArrayList<>();
ModelAndView view = new ModelAndView();
view.setViewName("index");
Map<String, List<OperationDto>> dataSource = util.init();
List<OperationDto> list = dataSource.get(state);
for (int i = 0; i < list.size(); i++) {
OperationDto operationDto = list.get(i);
operationDto.setName(operationMap.get(list.get(i).getCode()));
operationDto.setUrl("http://localhost:8080/"+list.get(i).getCode()+"?operation="+list.get(i).getCode()+"&state="+operationDto.getTo());
dtos.add(operationDto);
}
view.addObject("state",stateMap.get(state));
view.addObject("operationList",dtos);
return view;
}
@GetMapping("/yangshangchenggong")
public ModelAndView yangshangchenggong(@RequestParam String operation,@RequestParam String state) {
ArrayList<OperationDto> dtos = new ArrayList<>();
ModelAndView view = new ModelAndView();
view.setViewName("index");
Map<String, List<OperationDto>> dataSource = util.init();
List<OperationDto> list = dataSource.get(state);
for (int i = 0; i < list.size(); i++) {
OperationDto operationDto = list.get(i);
operationDto.setName(operationMap.get(list.get(i).getCode()));
operationDto.setUrl("http://localhost:8080/"+list.get(i).getCode()+"?operation="+list.get(i).getCode()+"&state="+operationDto.getTo());
dtos.add(operationDto);
}
view.addObject("state",stateMap.get(state));
view.addObject("operationList",dtos);
return view;
}
@GetMapping("/yangshangshibai")
public ModelAndView yangshangshibai(@RequestParam String operation,@RequestParam String state) {
ArrayList<OperationDto> dtos = new ArrayList<>();
ModelAndView view = new ModelAndView();
view.setViewName("index");
Map<String, List<OperationDto>> dataSource = util.init();
List<OperationDto> list = dataSource.get(state);
for (int i = 0; i < list.size(); i++) {
OperationDto operationDto = list.get(i);
operationDto.setName(operationMap.get(list.get(i).getCode()));
operationDto.setUrl("http://localhost:8080/"+list.get(i).getCode()+"?operation="+list.get(i).getCode()+"&state="+operationDto.getTo());
dtos.add(operationDto);
}
view.addObject("state",stateMap.get(state));
view.addObject("operationList",dtos);
return view;
}
@GetMapping("/lingdaobumanyiduini")
public ModelAndView lingdaobumanyiduini(@RequestParam String operation,@RequestParam String state) {
ArrayList<OperationDto> dtos = new ArrayList<>();
ModelAndView view = new ModelAndView();
view.setViewName("index");
Map<String, List<OperationDto>> dataSource = util.init();
List<OperationDto> list = dataSource.get(state);
for (int i = 0; i < list.size(); i++) {
OperationDto operationDto = list.get(i);
operationDto.setName(operationMap.get(list.get(i).getCode()));
operationDto.setUrl("http://localhost:8080/"+list.get(i).getCode()+"?operation="+list.get(i).getCode()+"&state="+operationDto.getTo());
dtos.add(operationDto);
}
view.addObject("state",stateMap.get(state));
view.addObject("operationList",dtos);
return view;
}
@GetMapping("/yangshangchenggonggaixuqiuchenggong")
public ModelAndView yangshangchenggonggaixuqiuchenggong(@RequestParam String operation,@RequestParam String state) {
ArrayList<OperationDto> dtos = new ArrayList<>();
ModelAndView view = new ModelAndView();
view.setViewName("index");
Map<String, List<OperationDto>> dataSource = util.init();
List<OperationDto> list = dataSource.get(state);
for (int i = 0; i < list.size(); i++) {
OperationDto operationDto = list.get(i);
operationDto.setName(operationMap.get(list.get(i).getCode()));
operationDto.setUrl("http://localhost:8080/"+list.get(i).getCode()+"?operation="+list.get(i).getCode()+"&state="+operationDto.getTo());
dtos.add(operationDto);
}
view.addObject("state",stateMap.get(state));
view.addObject("operationList",dtos);
return view;
}
@GetMapping("/gaixuqiuchenggong")
public ModelAndView gaixuqiuchenggong(@RequestParam String operation,@RequestParam String state) {
ArrayList<OperationDto> dtos = new ArrayList<>();
ModelAndView view = new ModelAndView();
view.setViewName("index");
Map<String, List<OperationDto>> dataSource = util.init();
List<OperationDto> list = dataSource.get(state);
for (int i = 0; i < list.size(); i++) {
OperationDto operationDto = list.get(i);
operationDto.setName(operationMap.get(list.get(i).getCode()));
operationDto.setUrl("http://localhost:8080/"+list.get(i).getCode()+"?operation="+list.get(i).getCode()+"&state="+operationDto.getTo());
dtos.add(operationDto);
}
view.addObject("state",stateMap.get(state));
view.addObject("operationList",dtos);
return view;
}
@GetMapping("/lingdaobumanyi")
public ModelAndView lingdaobumanyi(@RequestParam String operation,@RequestParam String state) {
ArrayList<OperationDto> dtos = new ArrayList<>();
ModelAndView view = new ModelAndView();
view.setViewName("index");
Map<String, List<OperationDto>> dataSource = util.init();
List<OperationDto> list = dataSource.get(state);
for (int i = 0; i < list.size(); i++) {
OperationDto operationDto = list.get(i);
operationDto.setName(operationMap.get(list.get(i).getCode()));
operationDto.setUrl("http://localhost:8080/"+list.get(i).getCode()+"?operation="+list.get(i).getCode()+"&state="+operationDto.getTo());
dtos.add(operationDto);
}
view.addObject("state",stateMap.get(state));
view.addObject("operationList",dtos);
return view;
}
@GetMapping("/gaixuqiu")
public ModelAndView gaixuqiu(@RequestParam String operation,@RequestParam String state) {
System.out.println("被开发打...");
ArrayList<OperationDto> dtos = new ArrayList<>();
ModelAndView view = new ModelAndView();
view.setViewName("index");
Map<String, List<OperationDto>> dataSource = util.init();
List<OperationDto> list = dataSource.get(state);
for (int i = 0; i < list.size(); i++) {
OperationDto operationDto = list.get(i);
operationDto.setName(operationMap.get(list.get(i).getCode()));
operationDto.setUrl("http://localhost:8080/"+list.get(i).getCode()+"?operation="+list.get(i).getCode()+"&state="+operationDto.getTo());
dtos.add(operationDto);
}
view.addObject("state",stateMap.get(state));
view.addObject("operationList",dtos);
return view;
}
@GetMapping("/siwang")
public ModelAndView siwang(@RequestParam String operation,@RequestParam String state) {
ArrayList<OperationDto> dtos = new ArrayList<>();
ModelAndView view = new ModelAndView();
view.setViewName("index");
Map<String, List<OperationDto>> dataSource = util.init();
List<OperationDto> list = dataSource.get(state);
for (int i = 0; i < list.size(); i++) {
OperationDto operationDto = list.get(i);
operationDto.setName(operationMap.get(list.get(i).getCode()));
operationDto.setUrl("http://localhost:8080/"+list.get(i).getCode()+"?operation="+list.get(i).getCode()+"&state="+operationDto.getTo());
dtos.add(operationDto);
}
view.addObject("state",stateMap.get(state));
view.addObject("operationList",dtos);
return view;
}
}
公司的状态机也是通过这种xml形式实现的,其实后来想想也可以通过枚举类实现。核心思想不变,只是换了实现方式而已。