1.需求背景介绍
公司内部每一次提新需求,我们都会走一次变更流程,而现在我们想把变更平台化,开发一套变更平台,其中有一个变更模型–变更单(ChangeOrder),变更单有自己的变更状态;
变更单状态描述如下:
状态变更只能按照箭头的方向,不能逆向、不能跳跃;
刚接到这个需求的时候就想到了状态机,所以就用状态机设计了一把;
2. 状态机类图
主题思想:
先定义一个状态(State)接口,使用不同的状态来创建不同的类继承该接口,再定义一个状态环境类(StateContent),代表当前程序的状态;每个状态子类都有一个方法(changeState())来改变当前环境的状态到指定的状态;并且StateContent可以保护对State及子类的直接访问,整个变更过程只需操作StateContent即可;
3.数据模型
/**
* 变更单
*/
public class ChangeOrder {
private Integer id;
private String state;
// 剩余业务字段略
}
// 状态机相关
@Data
@AllArgsConstructor
@NoArgsConstructor
public class StateContent {
private State state;
public void nextState(String stateName){
state.changeState(this, stateName);
}
public String getStateName() {
return state.getName();
}
}
// 状态接口
public interface State {
/**
* 获取状态机的名字
* @return
*/
String getName();
/**
* 根据状态的名字变更到不同的状态
* @param stateName
* @param content
* @return
*/
void changeState(StateContent content, String stateName);
}
/**
* “评估中”只能变更为“待变更状态”
* @author 辅助
* @version 1.0
* @date 2021/3/22 16:13
*/
public class UnderEvaluation implements State {
@Override
public String getName() {
return StateEnums.UNDEREVALUATION.name();
}
@Override
public void changeState(StateContent content, String stateName) {
if (StateEnums.TOCHANGED.name().equals(stateName)){
content.setState(new ToChanged());
}else {
throw new RuntimeException("不支持的状态变更");
}
}
}
/**
* “待评估状态”只能变更为“评估中”状态
* @author 辅助
* @version 1.0
* @date 2021/3/22 16:13
*/
public class ToEvaluate implements State {
@Override
public String getName() {
return StateEnums.TOEVALUATE.name();
}
@Override
public void changeState(StateContent content, String stateName) {
if (StateEnums.UNDEREVALUATION.name().equals(stateName)){
// 如果传入的下一个状态为评估中,则变更状态
content.setState(new UnderEvaluation());
}else {
throw new RuntimeException("不支持的状态变更");
}
}
}
/**
* “待变更”只能转变为“变更中” “待评估状态”
* @author 辅助
* @version 1.0
* @date 2021/3/22 16:13
*/
public class ToChanged implements State {
@Override
public String getName() {
return StateEnums.TOCHANGED.name();
}
@Override
public void changeState(StateContent content, String stateName) {
if (StateEnums.UNDEREVALUATION.name().equals(stateName)){
// 如果传入的下一个状态为评估中,则变更状态
content.setState(new UnderEvaluation());
}else {
throw new RuntimeException("不支持的状态变更");
}
}
}
/**
* “变更中”状态,只能变更为"待评估", "变更完成"状态
*
* @author 辅助
* @version 1.0
* @date 2021/3/21 23:45
*/
public class Changing implements State {
@Override
public String getName() {
return StateEnums.CHANGING.name();
}
@Override
public void changeState(StateContent content, String stateName) {
if (StateEnums.TOEVALUATE.name().equals(stateName)){
// 待评估
content.setState(new ToEvaluate());
}else if (StateEnums.CHANGECOMPLETED.name().equals(stateName)){
// 变更完成
content.setState(new ChangeCompleted());
}else {
throw new RuntimeException("不支持的状态变更");
}
}
}
/**
* “变更完成”状态,不能变更为其他状态
* @author 辅助
* @version 1.0
* @date 2021/3/22 16:14
*/
public class ChangeCompleted implements State {
@Override
public String getName() {
return StateEnums.CHANGECOMPLETED.name();
}
@Override
public void changeState(StateContent content, String stateName) {
throw new RuntimeException("状态已到终点,不可变更为其他状态");
}
}
4.业务实现
controller收到变更单更新请求,service取mapper查询当前变更单状态,然后进行变更单的状态变更;
controller
@RestController
@RequestMapping("/fuzhu/changeOrder")
public class ChangeOrderController {
@Autowired
private ChangeOrderService changeOrderService;
@PutMapping("/update")
public ResultInfo updateChangeOrderState(@RequestBody UpdateChangeOrderStateRequest request) {
return changeOrderService.updateChangeOrderState(request);
}
}
service
@Service
public class ChangeOrderServiceImpl implements ChangeOrderService {
@Autowired
private ChangeOrderMapper changeOrderMapper;
@Transactional
@Override
public ResultInfo updateChangeOrderState(UpdateChangeOrderStateRequest updateParam) {
// 获取变更单
ChangeOrder changeOrder = changeOrderMapper.queryById(updateParam.getId());
System.out.printf("变更前状态" + changeOrder);
// 获取状态上下文
StateContent stateContent = StateEnums.getStateContent(changeOrder.getState());
// 状态变更为目标状态
stateContent.nextState(updateParam.getNextState());
changeOrder.setState(stateContent.getStateName());
changeOrderMapper.insert(changeOrder);
System.out.printf("变更后状态" + changeOrder);
return ResultInfo.buildSuccess("success");
}
}
dal
@Repository
public class ChangeOrderMapperImpl implements ChangeOrderMapper {
/**
* 不连接数据库,这里为测试集
*/
private Map<Integer, ChangeOrder> mysql = new HashMap<>();
public ChangeOrderMapperImpl() {
mysql.put(1, new ChangeOrder(1, StateEnums.TOEVALUATE.name()));
mysql.put(2, new ChangeOrder(2, StateEnums.UNDEREVALUATION.name()));
mysql.put(3, new ChangeOrder(3, StateEnums.TOCHANGED.name()));
mysql.put(4, new ChangeOrder(4, StateEnums.CHANGING.name()));
mysql.put(5, new ChangeOrder(5, StateEnums.CHANGECOMPLETED.name()));
}
@Override
public ChangeOrder queryById(Integer id) {
return mysql.get(id);
}
@Override
public int insert(ChangeOrder changeOrder) {
mysql.put(changeOrder.getId(), changeOrder);
return 1;
}
}
测试集
不通过
通过
5.对状态机的理解
以上就是我公司对状态机的一个简单应用,优点很明显;
1:避免了大量if else 判断,使业务代码与状态变更代码相分离;
2:便于扩展,后期如果加上一个状态,如 “变更问题处理中”,业务层代码不用修改,直接写个类继承State接口,实现里面的方法即可;
个人能力有限,难免会有错误的地方,欢迎多多指出;
状态机核心代码位置:状态机的核心代码
代码库地址:设计模式