(给ImportNew加星标,提高Java技能)
今天的话题,我们从一个案例开始谈起。
if (状态 = “客户已确认”) {
if (操作行为 = “推送结算”) {
pushToSettle();
} else {
throw new UnsupportedOperationException(“客户已确认状态下不能操作除推送结算以外的其他操作”);
}
} else if (状态 = 其他XXX) {
// 其他判断处理…
}
/**
* 在当前状态下执行某个事件
*
* @param event 事件
* @return 若执行成功则返回变更后的新状态
* @throws UnsupportedOperationException 如果当前状态不支持该操作则抛出此异常
*/
State onEvent(Event event) throws UnsupportedOperationException;
/**
* 当前的状态
*
* @return
*/
State getState();
/**
* 当前状态可执行的事件清单
*
* @return
*/
List < Event > acceptableEvents();
/**
* 当前状态是否可以执行指定的某个事件(仅给出是否允许执行的判断结论,不会真的执行)
*
* @param event 事件
* @return
*/
boolean canPerformEvent(Event event);
/**
* 初始化一个状态机
*
* @param initialState 初始状态
* @param transitions 状态与事件之间的转换关系
*/
public SimpleFSMFrame(State initialState, Transition[] transitions) {
state = initialState;
this.transitionBox = new TransitionBox(transitions);
}
import java.util.*;
import java.util.stream.Collectors;
/**
* 轻量级的状态机框架,通过集成此类可快速实现一个简易的有限状态机。
* <br>
* 线程安全
*
* @author xieyipei
* @date 2021/8/13 18:13
*/
public class SimpleFSMFrame implements StateMachine {
/**
* 存放有当前状态机中的状态与事件转换关系的box
*/
private final TransitionBox transitionBox;
/**
* 状态机当前状态
*/
private State state;
/**
* 初始化一个状态机
*
* @param initialState 初始状态
* @param transitions 状态与事件之间的转换关系
*/
public SimpleFSMFrame(State initialState, Transition[] transitions) {
state = initialState;
this.transitionBox = new TransitionBox(transitions);
}
@Override
synchronized public State onEvent(Event event) throws UnsupportedOperationException {
state = execute(state, event);
return state;
}
@Override
public State getState() {
return state;
}
@Override
public List < Event > acceptableEvents() {
return acceptableEvents(state);
}
@Override
public boolean canPerformEvent(Event event) {
return canPerformEvent(state, event);
}
/**
* 在指定状态下执行某个事件,执行成功返回变更后的新状态
*
* @param currentState 状态
* @param event 事件
* @return 变更后的新状态
* @throws UnsupportedOperationException 如果当前状态不支持该操作则抛出此异常
*/
private State execute(State currentState, Event event) throws UnsupportedOperationException {
List < Transition > transitions = transitionBox.getTransitionBySource(currentState);
return transitions
.stream()
.filter(transition - > transition.getEvent().equals(event))
.findAny()
.orElseThrow(() - > new UnsupportedOperationException("Event:" + event.name() + " can not be performed on State:" + currentState.name()))
.getTarget();
}
/**
* 当前状态可执行的事件清单
*
* @param state 状态
* @return
*/
private List < Event > acceptableEvents(State state) {
List < Transition > transitions = transitionBox.getTransitionBySource(state);
return transitions
.stream()
.map(transition - > transition.getEvent())
.collect(Collectors.toList());
}
/**
* 当前状态是否可以执行指定的某个事件(仅给出是否允许执行的判断结论,不会真的执行)
*
* @param state 状态
* @param event 事件
* @return
*/
private boolean canPerformEvent(State state, Event event) {
List < Transition > transitions = transitionBox.getTransitionBySource(state);
return transitions
.stream()
.anyMatch(transition - > transition.getEvent().equals(event));
}
/**
* 检验状态与事件转换关系是否合法
*
* @param transitions
* @throws IllegalArgumentException 如果校验不通过则抛出此异常
*/
private void verifyTransition(Transition[] transitions) throws IllegalArgumentException {
//检查源状态+事件不能重复
Set < String > set = new HashSet < > ();
for (Transition transition: transitions) {
String key = transition.getSource().name() + "" + transition.getEvent().name();
boolean flag = set.add(key);
if (!flag)
throw new IllegalArgumentException(String.format("reduplicate transition source=%s event=%s", transition.getSource().name(), transition.getEvent().name()));
}
}
/**
* 存放整理后的状态与事件转换关系,并提供相应的访问方法
*/
private class TransitionBox {
private Map < State, List < Transition >> sourceMap = new HashMap < > ();
private Map < State, List < Transition >> targetMap = new HashMap < > ();
private Map < Event, List < Transition >> eventMap = new HashMap < > ();
/**
* 根据状态与事件的转换关系初始化一个box
*
* @param transitions 状态与事件的转换关系
*/
public TransitionBox(Transition[] transitions) {
//校验转换关系是否存在异常情况,如果存在则抛出异常
verifyTransition(transitions);
for (Transition transition: transitions) {
//sourceMap
List < Transition > sourceList = sourceMap.get(transition.getSource());
if (sourceList == null) {
sourceList = new ArrayList < > ();
sourceMap.put(transition.getSource(), sourceList);
}
sourceList.add(transition);
//targetMap
List < Transition > targetList = targetMap.get(transition.getTarget());
if (targetList == null) {
targetList = new ArrayList < > ();
targetMap.put(transition.getTarget(), targetList);
}
targetList.add(transition);
//eventMap
List < Transition > eventList = eventMap.get(transition.getEvent());
if (eventList == null) {
eventList = new ArrayList < > ();
eventMap.put(transition.getEvent(), eventList);
}
eventList.add(transition);
}
}
/**
* 获取指定源状态的所有转换关系
*
* @param source 源状态
* @return
*/
public List < Transition > getTransitionBySource(State source) {
List < Transition > list = sourceMap.get(source);
return list != null ? list : new ArrayList < > ();
}
/**
* 获取指定目标状态的所有转换关系
*
* @param target 目标状态
* @return
*/
public List < Transition > getTransitionByTarget(State target) {
List < Transition > list = targetMap.get(target);
return list != null ? list : new ArrayList < > ();
}
/**
* 获取与指定事件相关的所有转换关系
*
* @param event 事件
* @return
*/
public List < Transition > getTransitionByEvent(Event event) {
List < Transition > list = eventMap.get(event);
return list != null ? list : new ArrayList < > ();
}
}
}
/**
* 适用于海外应收账单状态(相比跨境应收增加了3个新状态)
*
* @author xieyipei
* @date 2021/9/23 14:57
*/
public class ARBillStateMachine extends SimpleFSMFrame {
/**
* 初始化一个状态机
*
* @param initialState 初始状态
*/
public ARBillStateMachine(State initialState) {
//调用自定义的状态转换关系枚举的values()方法获取到全部转换关系,然后传给父类的构造方法
super(initialState, ARTransition.values());
}
@Getter
private enum ARTransition implements Transition {
//状态转换关系通过枚举值形式配置出来。形式为:sourceState+event+targetState
T111(BillState.INIT, BillEvent.CONFIRM, BillState.MERCHANT_CLIENT_PENDING),
T121(BillState.INIT, BillEvent.DISCARD, BillState.DISCARDED),
T211(BillState.MERCHANT_CLIENT_PENDING, BillEvent.CLIENT_REJECT, BillState.OPERATING_PENDING),
T212(BillState.MERCHANT_CLIENT_PENDING, BillEvent.MERCHANT_CLIENT_REJECT, BillState.OPERATING_PENDING),
T213(BillState.MERCHANT_CLIENT_PENDING, BillEvent.ON_BEHALF_OF_CLIENT_REJECT, BillState.OPERATING_PENDING),
T214(BillState.MERCHANT_CLIENT_PENDING, BillEvent.ON_BEHALF_OF_MERCHANT_CLIENT_REJECT, BillState.OPERATING_PENDING),
T221(BillState.MERCHANT_CLIENT_PENDING, BillEvent.CLIENT_ACCEPT, BillState.MERCHANT_CLIENT_CONFIRMED),
T222(BillState.MERCHANT_CLIENT_PENDING, BillEvent.MERCHANT_CLIENT_ACCEPT, BillState.MERCHANT_CLIENT_CONFIRMED),
T223(BillState.MERCHANT_CLIENT_PENDING, BillEvent.ON_BEHALF_OF_CLIENT_ACCEPT, BillState.MERCHANT_CLIENT_CONFIRMED),
T224(BillState.MERCHANT_CLIENT_PENDING, BillEvent.ON_BEHALF_OF_MERCHANT_CLIENT_ACCEPT, BillState.MERCHANT_CLIENT_CONFIRMED),
T311(BillState.OPERATING_PENDING, BillEvent.DISCARD, BillState.DISCARDED),
T321(BillState.OPERATING_PENDING, BillEvent.CONFIRM, BillState.MERCHANT_CLIENT_PENDING),
T411(BillState.MERCHANT_CLIENT_CONFIRMED, BillEvent.PUSH_TO_SETTLE, BillState.SETTLEMENT_PENDING),
T421(BillState.MERCHANT_CLIENT_CONFIRMED, BillEvent.DISCARD, BillState.DISCARDED),
T511(BillState.SETTLEMENT_PENDING, BillEvent.PARTIAL_PAYMENT_WAS_RECEIVED, BillState.PARTIAL_PAYMENT_WAS_RECEIVED),
T521(BillState.SETTLEMENT_PENDING, BillEvent.OPERATING_CANCEL, BillState.FINANCIAL_REJECTED),
T522(BillState.SETTLEMENT_PENDING, BillEvent.FINANCIAL_REJECT, BillState.FINANCIAL_REJECTED),
T523(BillState.SETTLEMENT_PENDING, BillEvent.REJECT_IN_SETTLEMENT, BillState.FINANCIAL_REJECTED),
T531(BillState.SETTLEMENT_PENDING, BillEvent.COMPLETE_SETTLEMENT, BillState.SETTLEMENT_FINISHED),
T533(BillState.SETTLEMENT_PENDING, BillEvent.PUSH_TO_SETTLE, BillState.SETTLEMENT_PENDING),
T611(BillState.PARTIAL_PAYMENT_WAS_RECEIVED, BillEvent.FULL_PAYMENT_WAS_RECEIVED, BillState.SETTLEMENT_FINISHED),
T612(BillState.PARTIAL_PAYMENT_WAS_RECEIVED, BillEvent.PARTIAL_PAYMENT_WAS_RECEIVED, BillState.PARTIAL_PAYMENT_WAS_RECEIVED),
T613(BillState.PARTIAL_PAYMENT_WAS_RECEIVED, BillEvent.COMPLETE_SETTLEMENT, BillState.SETTLEMENT_FINISHED),
T711(BillState.FINANCIAL_REJECTED, BillEvent.DISCARD, BillState.DISCARDED), ;
private final State source;
private final State target;
private final Event event;
ARTransition(State source, Event event, State target) {
this.source = source;
this.target = target;
this.event = event;
}
}
}
private boolean canPerformEvent(Bill bill, BillEvent billEvent) {
//根据账单状态初始化状态机
StateMachine stateMachine = new ARBillStateMachine(bill.getBillState());
//通过状态机判断是否允许操作指定的行为
return stateMachine.canPerformEvent(billEvent);
}
转自:谢益培 / 京东物流,
链接:blog.csdn.net/jdcdev_/article/details/133698328
- EOF -
推荐阅读 点击标题可跳转3、Java 生鲜电商平台 - API 接口设计之 token、timestamp、sign 具体架构与实现
看完本文有收获?请转发分享给更多人
关注「ImportNew」,提升Java技能
点赞和在看就是最大的支持❤️