I created a library for defining executable state machines in your code. This includes the possibility to define hierarchical state machines with sub states. State machines simplify understanding how your code works when you have complex business rules.
As an oversimplified example for learning purposes, look at the following state machine for a shopping cart.
It represents its two fundamental states of being either empty, or non-empty.
The AddItem trigger causes a change to the non-empty cart state.
The RemoveItem trigger causes a change to the empty cart state, if the cart only contains 1 item.
And here's how the state machine is presented in code:
State<Cart, Trigger> emptyCartState = state("Empty Cart", cart -> cart != null && cart.items().size() == 0);
State<Cart, Trigger> nonEmptyCartState = state("Non-Empty Cart", cart -> cart != null && cart.items().size() > 0);
Statemachine<Cart, Trigger> statemachine = Statemachine.builder()
.states(emptyCartState,nonEmptyCartState)
.transitions(
transition(anyState(), nonEmptyCartState,
when(AddItem.class, consumeWith(Cart::addItem))),
transition(nonEmptyCartState, nonEmptyCartState,
whenInCase(RemoveItem.class, i -> i.state().items().size() > 1, supplyWith(Cart::removeItem))),
transition(nonEmptyCartState, emptyCartState,
whenInCase(RemoveItem.class, i -> i.state().items().size() == 1, supplyWith(Cart::removeItem)))
)
.flows(
entryFlow(when(CreateCart.class, init(Cart::createCart)))
)
.build();
Please visit the project on GitHub for details. I'm looking forward to your feedback.