需求1 简单的审核流程

动作
prev next
状态
新建 待审核 审核通过 退回修改
BO
@Data
public class BO {
private long id;
private String name;
private StatusEnum status;
}
StatusEnum
@Data
public enum StatusEnum {
INIT(1, "新建"),
WAITING(2, "待审核"),
PASSED(3, "审核通过"),
REJECT(4, "驳回修改");
}
if/else实现
// 下一步操作
next() {
if(status = "新建"){ status = "带审核"; return; }
if(status = "待审核"){ status = "审核通过"; return; }
if(status = "审核通过"){ throw new Excption("当前状态没有下一步操作") }
if(status = "退回修改"){ status = "退回"; return; }
}
// 上一步操作
prev() {
if(status = "新建"){ throw new Excption("当前状态没有上一步操作") }
if(status = "待审核"){ status = "退回修改"; return; }
if(status = "审核通过"){ throw new Excption("当前状态没有上一步操作") }
if(status = "退回修改"){ throw new Excption("当前状态没有上一步操作") }
}
- 优点:速度
- 缺点:不灵活
状态迁移表实现
| state | Event | new state | ||
|---|---|---|---|---|
| 新建 | prev | none | ||
| 新建 | next | 待审核 | ||
| 待审核 | prev | 退回修改 | ||
| 待审核 | next | 审核通过 | ||
| 审核通过 | prev | none | ||
| 审核通过 | next | none | ||
| 退回修改 | prev | none | ||
| 退回修改 | next | 待审核 |
- 优点:灵活,运行时改变状态机逻辑; 多个迁移表实现多种状态机逻辑
- 缺点:读数据库
状态模式State
参与者
- State接口
- 状态实现类
- Context (StateMachine.java)

按动作设计API
public interface DemoService {
// 下一步流程
String next(DTO reqDTO) throws PendingException;
// 上一步流程
String prev(DTO reqDTO) throws PendingException;
}
State接口
public interface State {
void actionToPrev() throws PendingException;
void actionToNext() throws PendingException;
}
State实现类
InitState PassedState RejectState WaitingState
/**
* 应用状态:等待审核
*/
@Service("WAITING_STATE")
public class WaitingState implements State {
@Setter
@Autowired
private StateMachine stateMachine;
/**
* 审核不通过
*/
@Override
public void actionToPrev() throws PendingException {
// Context获取当前的BO
BO bo = stateMachine.getBO();
// 更新状态
bo.setStatus("退回修改");
}
/**
* 审核通过
*/
@Override
public void actionToNext() throws PendingException {
// Context获取当前的BO
BO bo = stateMachine.getBO();
// 更新状态
bo.setStatus("审核通过");
// 如果有需要, 可以通过Context状态转换, 继续执行新State下的action
stateMachine.setBO(bo);
}
}
StateMachine
@Data
@Service
public class StateMachine implements State {
@Autowired
private InitState initState;
@Autowired
private WaitingState waitingState;
@Autowired
private RejectState rejectState;
@Autowired
private PassedState passedState;
private State state;
@Getter
private BO bo;
@Override
public void actionToPrev() throws PendingException {
state.actionToPrev();
}
@Override
public void actionToNext() throws PendingException {
state.actionToNext();
}
// Context这里负责状态的转换: 当传入BO的时候,根据status选择State实现类
public void setBO(BO bo) throws PendingException {
this.bo = bo;
StatusEnum status = this.bo.getStatus();
if (status == "新建") {
this.state = initState;
return;
}
if (status == "审核通过") {
this.state = passedState;
return;
}
if ((status == "待审核") {
this.state = waitingState;
return;
}
if ((status == "退回修改") {
this.state = rejectState;
return;
}
throw new PendingException(MerCodeEnum.M1060);
}
}
用法
public class Demo implements DemoService {
@Autowired
private BoDAO boDAO;
@Autowired
private StateMachine stateMachine;
@Override
public void prev(PrevStateReqDTO reqDTO) throws PendingException {
BO bo = boDAO.selectById(reqDTO.getId());
// 状态的转换, 选择State实现类
stateMachine.setBO(bo);
// 执行动作
stateMachine.actionToPrev();
// 入库操作
......
}
@Override
public void next(NextStateReqDTO reqDTO) throws PendingException {
BO bo = boDAO.selectById(reqDTO.getId());
// 状态的转换, 选择State实现类
stateMachine.setBO(bo);
// 执行动作
stateMachine.actionToNext();
}
}
谁定义状态的转换
1. 可以在Conetxt中定义转态转换, 即Conetxt需要注入所有State接口的实体类
见上面的示例中的StateMachine类的setBO()方法
2. 可以在State子类中指定后续转态转换,缺点:子类拥有其他子类的信息
State接口
public interface State {
void updateStatus(BO bo) throws PendingException;
void actionToPrev() throws PendingException;
void actionToNext() throws PendingException;
}
StateMachine
@Data
@Service
public class StateMachine implements State {
// 不需要注入所有的State实现类
private State state;
private BO bo;
@Override
public void updateStatus(BO bo) throws PendingException {
// 状态转换
state.updateStatus(bo);
}
@Override
public void actionToPrev() throws PendingException {
state.actionToPrev(bo);
}
@Override
public void actionToNext(BO bo) throws PendingException {
state.actionToNext(bo);
}
}
WaitingState实现类
/**
* 应用状态:等待审核
*/
@Service("WAITING")
public class WaitingState implements State {
@Setter
@Autowired
private StateMachine stateMachine;
// 子类注入其他子类的信息
@Autowired
private RejectdState rejectdState;
@Autowired
private PassedState passedState;
@Override
public void updateStatus(BO bo) throws PendingException {
// 状态转换
bo.setState("等待审核");
}
/**
* 审核不通过
*/
@Override
public void actionToPrev() throws PendingException {
// 子类负责状态转换, 直接指定State的实现类
stateMachine.setState(rejectdState);
// 更新状态
stateMachine.updateStatus();
}
/**
* 审核通过
*/
@Override
public void actionToNext() throws PendingException {
// 状态转换
stateMachine.setState(passedState);
// 更新状态
stateMachine.updateStatus();
}
}
状态模式优缺点
- 优点:效率高 + 灵活
- 缺点:类比较多
- API设计风险点: 同一个人不小心多次调用prev接口, 会导致流程到达终态
优化代码v2 开闭原则
- 对修改关闭
不用修改已有代码
- 对扩展开放
只需新增一个状态类
StateMachine
@Data
@Service
public class StateMachine implements State {
// @Autowired
// private WaitingState waitingState;
// @Autowired
// private RejectState rejectState;
// @Autowired
// private PassedState passedState;
@Autowired
private Map<String, State> stateMap;
}
State实现类
@Service("...")用于指定stateMap中的key
@Service("WAITING_STATE")
public class WaitingState implements State {
......
}
@Service("PASSED_STATE")
public class PassedState implements State {
......
}
优化代码v3 并发场景
StateMachine原型模式
@Scope("prototype") 表示每次获得bean都会生成一个新的对象
@Data
@Service
@Scope("prototype")
public class StateMachine implements State {
@Autowired
private Map<String, State> stateMap;
private State state;
private BO bo;
......
}
StateMachine 单例模式
删除公共字段
@Data
@Service
public class StateMachine implements State {
private final static String _SERVICE = "%s_STATE";
@Autowired
private Map<String, State> stateMap;
@Override
public void actionToPrev(BO bo) throws PendingException {
// 当前状态
StatusEnum status = bo.getStatus();
stateMap.get(String.format(_SERVICE, status.name())).actionToPrev();
}
@Override
public void actionToNext(BO bo) throws PendingException {
// 当前状态
StatusEnum status = bo.getStatus();
stateMap.get(String.format(_SERVICE, status.name())).actionToNext();
}
}
State接口
StateMachine是单例时, 由于没有缓存BO, 所以BO要作为参数传入State接口
public interface State {
void actionToPrev(BO bo) throws PendingException;
void actionToNext(BO bo) throws PendingException;
}
需求2 在审核通过后再加一个 停用 状态

ClosedState实现类
新增一个State实现类ClosedState
/**
* 应用状态:停用
*/
@Service("CLOSED")
public class ClosedState implements State {
@Setter
@Autowired
private StateMachine stateMachine;
/**
* next
*/
@Override
public void actionToNext() throws PendingException {
// Context获取当前的BO
BO bo = stateMachine.getBO();
// 更新状态
bo.setStatus("待审核");
}
}
修改StatusEnum
@Data
public enum StatusEnum {
......,
CLOSED(5, "停用");
}
修改PassedState实现类
/**
* 应用状态:审核通过
*/
@Service("PASSED")
public class PassedState implements State {
@Setter
@Autowired
private StateMachine stateMachine;
/**
* prev
*/
@Override
public void actionToPrev() throws PendingException {
// Context获取当前的BO
BO bo = stateMachine.getBO();
// 更新状态
bo.setStatus("停用");
}
}
状态模式 VS 策略模式
-
状态模式

-
策略模式

需求3 一个动作下有多个状态
- 需求1是一个动作没有分支:prev -> success
- 需求2是一个动作存在分支:pay -> success | fail

按动作设计API
public interface DemoService {
// 下单
String build(DTO reqDTO) throws PendingException;
// 支付
// result: 成功 success 失败 fail
String pay(DTO reqDTO, ResultEnum result) throws PendingException;
// 担保
// result: 成功 success 失败 fail
String guarantee(DTO reqDTO, ResultEnum result) throws PendingException;
}
State接口
每个动作的大体的共同点是分为成功,失败
public interface State {
// 成功
String success() throws PendingException;
// 失败
String fail() throws PendingException;
// 更新状态值
String updateStatus() throws PendingException;
}
StateMachine
@Data
@Service
public class StateMachine implements State {
private final static String _SERVICE = "%s_STATE";
@Autowired
private Map<String, State> stateMap;
public void changeState(BO bo, ResultEnum result) throws PendingException {
if (ResultEnum.SUCCESS.equals(result)) {
this.success(bo);
return;
}
if (ResultEnum.FAIL.equals(result)) {
this.fail(bo);
return;
}
throw new PendingException(TradeCode.T0010);
}
@Override
public void updateStatus(BO bo) throws PendingException {
// 状态转换
stateMap.get(String.format(_SERVICE, bo.getStatus().name())).updateStatus(bo);
}
@Override
public void success(BO bo) throws PendingException {
stateMap.get(String.format(_SERVICE, bo.getStatus().name())).actionToPrev(bo);
}
@Override
public void fail(BO bo) throws PendingException {
stateMap.get(String.format(_SERVICE, bo.getStatus().name())).actionToNext(bo);
}
}
用法
public class Demo impl DemoService {
// 下单
String build(DTO reqDTO) throws PendingException{
stateMachine.sucess(bo);
}
// 支付
// result: 成功 success 失败 fail
String pay(DTO reqDTO, ResultEnum result) throws PendingException {
// 根据result判断是执行success还是执行fail方法
stateMachine.changeState(bo, result.name());
}
}
需求4 双状态

降纬处理

- API设计风险点: 同一个人不小心多次调用prev接口, 会导致流程到达终态