状态机设计模式的应用

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接口,实现里面的方法即可;

个人能力有限,难免会有错误的地方,欢迎多多指出;

状态机核心代码位置:状态机的核心代码

代码库地址设计模式

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值