Stateless state machine
1: Foreword
In the project, there are often some work orders, applications and so on, which need to flow the status. This kind of demand is generally what conditions are met, and then the state is reversed. These processes are logically similar in structure and can be handled abstractly. Using a general structure can make the system cleaner and the code logic more single.
Ali found an open source, lightweight stateless machine component. After careful study, it is really suitable for decoupling code logic in this scenario. Compared with if else code, it is easier to understand and more elegant.
2: Model of state machine
- State: state
- Event: event. The status is triggered by the event and changes
- Transition: flow, indicating the transition from one state to another
- External Transition: External Transition, which is the transition between two different states
- Internal Transition: internal flow, flow between the same states
- Condition: condition, indicating whether a certain state is allowed to be reached
- Action: action. What can be done after reaching a certain state
- StateMachine: state machine
3: How to use
Here we assume that several simple logic needs to be processed:
- We need to transfer from the status to be approved to the status approved. Before approval, we need to make an audit verification. After approval, we need to send a text message to inform the user.
Realization of external status flow:
- Define status enumeration (to be approved, approved, rejected)
static enum States { WAITE, PASS, REJECT }
- Define an event enumeration, including approval event / approval rejection / application submission / internal status flow
static enum Events { AUDIT, AUDIT_REJECT, COMMIT, COMPLETE_INFORMATION } // Context object static class Context{ String operator = "flw"; String entityId = "7758258"; }
- Configure a state machine according to the above requirements
StateMachineBuilder<States, Events, Context> builder = StateMachineBuilderFactory.create(); builder.externalTransition() // External state flow .from(States.WAITE) // Start status: to be approved .to(States.PASS) // Purpose status: approved .on(Events.AUDIT) // Event: audit event .when(checkCondition()) // If the flow requires verification, doAction will not be performed if the verification fails .perform(doAction()); // Execute flow operation StateMachine<States, Events, Context> stateMachine = builder.build(MACHINE_ID); // Print the flow chart in the state machine stateMachine.showStateMachine(); private Condition<StateMachineMyTest.Context> checkCondition() { return (ctx) -> {return true;}; // Returns true by default } private Action<StateMachineMyTest.States, StateMachineMyTest.Events, StateMachineMyTest.Context> doAction() { return (from, to, event, ctx)->{ System.out.println(ctx.operator+" is operating "+ctx.entityId+" from:"+from+" to:"+to+" on:"+event); }; }
Print map results of execution state machine:
-----StateMachine:TestStateMachine------- State:PASS State:WAITE Transition:WAITE-[AUDIT, EXTERNAL]->PASS ------------------------
- Execution state machine
// Perform the audit operation of the status to be audited through the state machine, States target = stateMachine.fireEvent(States.WAITE, Events.AUDIT, new Context()); Assert.assertEquals(States.PASS, target); // Success will return to the target state machine, and failure will return to the original state
Execution results:
flw is operating 7758258 from:WAITE to:PASS on:AUDIT
If the checkCondition returns false, the doAction operation will not be executed.
Realization of internal state flow
- It is assumed that users only need to complete the data, and only need to update the data without status flow. This demand can be realized through internal state flow
@Test public void testExternalNormal(){ StateMachineBuilder<States, Events, Context> builder = StateMachineBuilderFactory.create(); builder.externalTransition()// Internal circulation .from(States.WAITE) .to(States.PASS) .on(Events.COMPLETE_INFORMATION) .when(checkCondition()) .perform(doAction()); StateMachine<States, Events, Context> stateMachine = builder.build(MACHINE_ID); // Print the flow chart in the state machine stateMachine.showStateMachine(); // Perform the audit operation of the status to be audited through the state machine, States target = stateMachine.fireEvent(States.WAITE, Events.AUDIT, new Context()); Assert.assertEquals(States.WAITE, target); }
Execution results:
-----StateMachine:TestStateMachine------- State:PASS State:WAITE Transition:WAITE-[COMPLETE_INFORMATION, EXTERNAL]->PASS ------------------------
Exceptions in the method will also be thrown, which also supports transaction operations.
The overall flow chart is as follows:
4: Summary
The state machine is very lightweight and supports transaction operations. For the scenario of state flow, the logical decoupling is relatively elegant.
Link: https://github.com/alibaba/COLA/tree/master/cola-components/cola-component-statemachine