设计模式实战03-状态模式+建造者模式
一.需求描述
在我们日常开发过程中,经常遇到单据状态扭转的需求, 比如评估单,申请单...等等,本文将基于状态机思想设计一个通用的代码框架来解决类似需求.
二.状态机简介
状态机是有限状态自动机的简称,是现实事物运行规则抽象而成的一个数学模型。状态机包含如下四个概念:
第一个是 State ,状态。定义一个事物所处的状况,一个状态机至少要包含两个状态。
第二个是 Event ,事件。事件就是执行某个操作的触发条件或者口令。
第三个是 Action ,动作。事件发生以后要执行动作。
第四个是 Transition ,变换(状态转移)。也就是从一个状态变化为另一个状态。定义transition就是在定义状态机的转移流程
下图是我对状态机的粗浅理解
三.状态机代码实现
通过上图分析,每个状态下可以发生多个事件, 每个事件又可以触发对应的状态转移, 在状态转移里执行定义好的Action. 通过分析可以得到一个双层MAP的状态机结构, 代码结构如下:
状态机定义了一个StateMap维护了所有的State, 每个State维护了一个event和Transition的映射关系,而每一个Transition里定义了要执行的Action以及源状态和目标状态及事件.
当触发一个事件的时候即调用StateMachine的fireEvent方法,根据源状态可以获取对应的State,然后再根据Event获取对应的Transition执行Action,执行后流转到Transition定义好的目标状态;
1.定义状态
定义状态相关接口
java
public interface State<S, E, C>{
S getId();
/**
* @param event 触发事件
* @param target 目标状态
* @return transition
*/
Transition<S, E, C> addTransition(E event, State<S, E, C> target);
/**
* @param event 触发事件
* @return 状态转移
*/
List<Transition<S, E, C>> getEventTransitions(E event);
}
定义状态相关接口实现
java
public class DefaultState<S, E, C> implements State<S, E, C> {
private final S stateId;
private final HashMap<E, List<Transition<S, E, C>>> eventTransitions = new HashMap<>();
DefaultState(S stateId) {
this.stateId = stateId;
}
@Override
public S getId() {
return stateId;
}
@Override
public Transition<S, E, C> addTransition(E event, State<S, E, C> target) {
Transition<S, E, C> transition = new Transition<>();
transition.setSource(this);
transition.setTarget(target);
transition.setEvent(event);
put(event, transition);
return transition;
}
@Override
public List<Transition<S, E, C>> getEventTransitions(E event) {
return eventTransitions.get(event);
}
private void put(E event, Transition<S, E, C> transition) {
List<Transition<S, E, C>> transitions = eventTransitions.get(event);
if (transitions == null) {
transitions = new ArrayList<>();
transitions.add(transition);
eventTransitions.put(event, transitions);
} else {
check(transitions, transition);
transitions.add(transition);
}
}
public List<Transition<S, E, C>> get(E event) {
return eventTransitions.get(event);
}
private void check(List<Transition<S, E, C>> existingTransitions, Transition<S, E, C> newTransition) {
for (Transition<S, E, C> transition : existingTransitions) {
if (transition.equals(newTransition)) {
throw new StateMachineException(newTransition + "already Exist!");
}
}
}
}
2.定义Action
java
public interface Action<S, E, C> {
/**
*
* @param from 源状态
* @param to 目标状态
* @param event 事件
* @param context 上下文参数
*/
void execute(S from, S to, E event, C context);
}
3.定义状态转移
java
@Getter
@Setter
public class Transition<S, E, C> {
private final Logger logger = LoggerFactory.getLogger(Transition.class);
/**
* 动作序列
*/
private final List<Action<S, E, C>> actions = new ArrayList<>();
/**
* 原始状态
*/
private State<S, E, C> source;
/**
* 触发事件
*/
private E event;
/**
* 目标状态
*/
private State<S, E, C> target;
/**
* 转移条件
*/
private Predicate<C> condition;
/**
* 最后执行的异步任务
*/
private Action<S, E, C> asyncTask;
/**
* 执行转移
*/
public State<S, E, C> transit(C ctx) {
if (actions.size() != 0) {
for (Action<S, E, C> action : actions) {
action.execute(source.getId(), target.getId(), event, ctx);
}
}
if (asyncTask != null) {
CompletableFuture.runAsync(() -> asyncTask.execute(source.getId(), target.getId(), event, ctx)).exceptionally(throwable -> {
logger.warn("Async task run error {}", throwable.getMessage());
return null;
});
}
return target;
}
}
4.组装状态机
定义状态机接口
java
public interface StateMachine<S, E, C>{
/**
* 获取状态机Id
*/
String getMachineId();
/**
* @param source 原始状态
* @param event 事件
* @param ctx 上下文
* @return target目标状态
*/
S fireEvent(S source, E event, C ctx);
}
状态机实现类
java
@Getter
@Setter
public class StateMachineImpl<S, E, C> implements StateMachine<S, E, C> {
//状态机State集合
private final Map<S, State<S, E, C>> stateMap;
//状态机id
private String machineId;
public StateMachineImpl(Map<S, State<S, E, C>> stateMap) {
this.stateMap = stateMap;
}
@Override
public S fireEvent(S source, E event, C ctx) {
Transition<S, E, C> transition = routeTransition(source, event, ctx);
if (transition == null) {
throw new StateMachineException("State machine route cannot find transition!");
}
return transition.transit(ctx).getId();
}
private Transition<S, E, C> routeTransition(S source, E event, C ctx) {
State<S, E, C> state = getState(source);
List<Transition<S, E, C>> transitions = state.getEventTransitions(event);
if (transitions == null || transitions.size() == 0) {
throw new StateMachineException("State machine can find right transition!");
}
Transition<S, E, C> transit = null;
for (Transition<S, E, C> transition : transitions) {
if (transition.getCondition() == null) {
transit = transition;
break;
} else if (transition.getCondition().test(ctx)) {
transit = transition;
break;
}
}
return transit;
}
private State<S, E, C> getState(S currentState) {
State<S, E, C> state = stateMap.get(currentState);
if (state == null) {
throw new StateMachineException(currentState + " is not found, please check state machine");
}
return state;
}
}
状态机的代码实现到这里 就差不多了,接下来我们采用建造者模式去构建状态转移和状态机
四.构建状态转移和状态机
在我们java中有一种非常优雅的代码,就是lambda表达式,采用流式编程和函数式接口来实现数据的处理,非常赏心悦目,接下来我将借鉴这种思想来实现状态转移和状态机的构建.主要是为了提供统一的构建方式;
1.构建状态机
定义接口
java
public interface StateMachineBuilder<S, E, C> {
TransitionBuilder<S, E, C> transitionConfigurer();
StateMachine<S, E, C> build(String machineId);
}
构建状态机实现
java
public class StateMachineBuilderImpl<S, E, C> implements StateMachineBuilder<S, E, C> {
private final Map<S, State<S, E, C>> stateMap = new ConcurrentHashMap<>();
private final StateMachineImpl<S, E, C> stateMachine = new StateMachineImpl<>(stateMap);
@Override
public TransitionBuilder<S, E, C> transitionConfigurer() {
return new TransitionBuilderImpl<>(stateMap);
}
@Override
public StateMachine<S, E, C> build(String machineId) {
stateMachine.setMachineId(machineId);
return stateMachine;
}
}
2.构建状态转移
定义构建状态转移相关接口
java
public interface TransitionBuilder<S, E, C> {
//设置源状态
TransitionBuilder<S, E, C> from(S... stateIds);
//设置目标状态
TransitionBuilder<S, E, C> to(S stateId);
//定义状态转移事件
TransitionBuilder<S, E, C> on(E event);
//定义发生状态转移的额外条件
TransitionBuilder<S, E, C> when(Predicate<C> condition);
//指定状态转移的Action
TransitionBuilder<S, E, C> perform(Action<S, E, C> action);
//指定状态转移的异步Action
TransitionBuilder<S, E, C> runAsync(Action<S, E, C> action);
//回到开始 的TransitionBuilder (构造一个新的Transition)
TransitionBuilder<S, E, C> and();
default void end() {
// do nothing
}
}
定义状态转移实现类
java
public class TransitionBuilderImpl<S, E, C> implements TransitionBuilder<S, E, C> {
private final Map<S, State<S, E, C>> stateMap;
protected State<S, E, C> target;
private List<State<S, E, C>> sources;
private List<Transition<S, E, C>> transitions;
public TransitionBuilderImpl(Map<S, State<S, E, C>> stateMap) {
this.stateMap = stateMap;
}
@Override
public TransitionBuilder<S, E, C> from(S... stateIds) {
sources = Arrays.stream(stateIds).distinct().map(stateId -> getState(stateMap, stateId)).collect(Collectors.toList());
return this;
}
@Override
public TransitionBuilder<S, E, C> to(S stateId) {
target = getState(stateMap, stateId);
return this;
}
@Override
public TransitionBuilder<S, E, C> on(E event) {
this.transitions = sources.stream().map(sources -> sources.addTransition(event, target)).collect(Collectors.toList());
return this;
}
@Override
public TransitionBuilder<S, E, C> when(Predicate<C> condition) {
for (Transition<S, E, C> transition : transitions) {
transition.setCondition(condition);
}
return this;
}
@Override
public TransitionBuilder<S, E, C> perform(Action<S, E, C> action) {
for (Transition<S, E, C> transition : transitions) {
transition.addAction(action);
}
return this;
}
@Override
public TransitionBuilder<S, E, C> runAsync(Action<S, E, C> action) {
for (Transition<S, E, C> transition : transitions) {
transition.setAsyncTask(action);
}
return this;
}
@Override
public TransitionBuilder<S, E, C> and() {
return this;
}
}
这样我们在真正构建状态机的时候可以采用如下方式进行,可以很清晰判断状态扭转规则:
java
StateMachineBuilder builder = new StateMachineBuilderImpl()
stateMachineBuilder.transitionConfigurer()
.from(MapSettlementAuditNodeEnum.INIT)
.to(MapSettlementAuditNodeEnum.SELLING_MANAGER_AUDIT)
.on(MapSettlementEventEnum.SUBMIT)
.when(context -> true)
.perform(auditBizService.generalAction())
.and()
- 首先在new StateMachineBuilderImpl的时候会创建好双层Map并放入状态机中
java
public class StateMachineBuilderImpl<S, E, C> implements StateMachineBuilder<S, E, C> {
private final Map<S, State<S, E, C>> stateMap = new ConcurrentHashMap<>();
private final StateMachineImpl<S, E, C> stateMachine = new StateMachineImpl<>(stateMap);
@Override
public TransitionBuilder<S, E, C> transitionConfigurer() {
return new TransitionBuilderImpl<>(stateMap);
}
@Override
public StateMachine<S, E, C> build(String machineId) {
stateMachine.setMachineId(machineId);
return stateMachine;
}
}
-
在调用stateMachineBuilder.transitionConfigurer()会创建TransitionBuilderImpl,并传入双层Map<S, State<S, E, C>> stateMap,后续的state对应的event和transitions映射都会构造好放入这个集合中;
-
在调用from方法定义来源状态的时候会调用一个getState方法,此方法实际上是在往stateMap新增State,并最终把构建的state集合赋值给了当前builder对象的sources字段(State集合),实现如下:
java
@Override
public TransitionBuilder<S, E, C> from(S... stateIds) {
sources = Arrays.stream(stateIds).distinct().map(stateId -> getState(stateMap, stateId)).collect(Collectors.toList());
return this;
}
public static <S, E, C> State<S, E, C> getState(Map<S, State<S, E, C>> stateMap, S stateId) {
State<S, E, C> state = stateMap.get(stateId);
if (state == null) {
state = new DefaultState<>(stateId);
stateMap.put(stateId, state);
}
return state;
}
- 在调用to方法的时候新增State,并最终赋值给了当前builder对象的target;
java
@Override
public TransitionBuilder<S, E, C> to(S stateId) {
target = getState(stateMap, stateId);
return this;
}
- 在调用on方法时,根据sources,target以及event构建transition,并放入state的eventTransitions中;并把sources当前Event件对应的transition集合赋值给当前builder对象的transitions.方便后续构造每个transition的Action和Condition;
java
@Override
public TransitionBuilder<S, E, C> on(E event) {
this.transitions = sources.stream().map(sources -> sources.addTransition(event, target)).collect(Collectors.toList());
return this;
}
- 在调用when方法时, 定义转移条件,在调用perform则设置transition的行为;
java
@Override
public TransitionBuilder<S, E, C> when(Predicate<C> condition) {
for (Transition<S, E, C> transition : transitions) {
transition.setCondition(condition);
}
return this;
}
@Override
public TransitionBuilder<S, E, C> perform(Action<S, E, C> action) {
for (Transition<S, E, C> transition : transitions) {
transition.addAction(action);
}
return this;
}
- 整个过程结束,State的event,transition关系映射已构建;
五.状态机使用
下面是状态机具体业务使用案例
1.配置状态机Bean
java
@Configuration
public class StateMachineConfig {
private final MapSettlementAuditBizService auditBizService;
public StateMachineConfig(MapSettlementAuditBizService mapSettlementAuditBizService) {
this.auditBizService = mapSettlementAuditBizService;
}
@Bean("mapSettlementStateMachine")
public StateMachine<MapSettlementAuditNodeEnum, MapSettlementEventEnum, MapSettlementContext> stateMachine() {
StateMachineBuilder<MapSettlementAuditNodeEnum, MapSettlementEventEnum, MapSettlementContext> stateMachineBuilder = new StateMachineBuilderImpl<>();
stateMachineBuilder.transitionConfigurer()
.from(MapSettlementAuditNodeEnum.INIT)
.to(MapSettlementAuditNodeEnum.SELLING_MANAGER_AUDIT)
.on(MapSettlementEventEnum.SUBMIT)
.when(context -> true)
.perform(auditBizService.generalAction())
.and()
.from(MapSettlementAuditNodeEnum.SELLING_MANAGER_AUDIT)
.to(MapSettlementAuditNodeEnum.SELLING_ASSISTANT_AUDIT)
.on(MapSettlementEventEnum.PASS)
.when(context -> true)
.perform(auditBizService.generalAction())
.and()
...
end();
return stateMachineBuilder.build("mapSettlementStateMachine");
}
//需要构建其他状态机,在下面再增加一个Bean就可以了
}
2.调用状态机方法
java
//获取业务单据当前状态, 当前事件,传入上下文参数
MapSettlementAuditNodeEnum targetNode = stateMachine.fireEvent(currentAuditNode, MapSettlementEventEnum.SUBMIT, settlementContext);
六.总结
当遇到涉及状态转移,节点审批等需求的时候,可以考虑状态机模式,可以提供一个通用的代码模版, 其主要作用让业务逻辑清晰可见,可维护性高,同时规范了代码行为.
本文的状态机实现不支持动态变更转移规则,适合不是特别复杂的业务需求. 目前面对特别复杂的流程审批等需求,一般都是采用成熟流程引擎平台,本文就不做拓展了;