dotnet-state-machine / stateless

A simple library for creating state machines in C# code
Other
5.53k stars 765 forks source link

Can the same operation be a substate of more than one state? #489

Open flycast opened 1 year ago

flycast commented 1 year ago

New to state machines and dotnet-state-machine. NOT an issue but a question. I have a path that looks like this from home:

Homed -> Automatic -> Automatic.Paused (substate of automatic) -> Purge
Homed -> Automatic -> Purge
Homed -> Purge
Homed -> Manual -> Purge

The purge is EXACTLY the same in all four cases. The only difference is where they came from. If a transition is made from any state to purge then when purge is finished it needs to go back the the state it came from. A purge can only return back to the state it came from ever. It never can go to another state.

How is this normally done? Do I have one state "Purge" and keep track of where it came from so when it's time to go back I make the transition back to the state it came from?

-OR-

Do I have four discrete substates for purge where they are each of substate of the state it came from?

I have tried charting this out with Purge as a stand alone state and the chart is quite messy. I have found that in cases like these it helps to understand what the "normal" design pattern is. Any help would be appreciated.

mclift commented 1 year ago

Hi,

I played around with this scenario and I think you may have identified an issue after all.

It's my understanding that each state should only appear once in the graph, so you'd need four separate states to represent this state machine.

When I tried out your example, it actually allowed me to add Purge as a substate of multiple parents, but the last substate to be configured won, i.e. when I tried Homed -> Auto -> Auto.Paused -> Auto.Purge, it actually transitioned to Manual.Purge. Even if this had worked, you'd need a separate trigger for each substate anyway because the same trigger can't have multiple destinations from the same state unless you've used PermitIf.

I need to give it some more thought, but I feel like the state machine should throw an exception if you attempt to add a state in multiple places like that. I'm still getting my head back into this project so if someone knows different, please comment!

An alternative approach you might like to consider is to create classes for your states. That way, you can have multiple instances of, say, a PurgeState class. There's a discussion about this approach in issue #234 with a link to a demo project.

I hope I understood the question correctly. Did this help?

Thanks

vlm--- commented 1 year ago

I faced the same issue when implementing a state machine where every superstate had the same or a subset of the same substates as an approval process. When found this bug, I tried to fix this behavior in Stateless itself, but it proved to be more complex than I thought and had the bandwidth to deal with at the time. I ended up with a design of two linked-up state machines. One for the superstates, and the other one for the substates. Both are listening for the same action triggers, but executing in a superstate machine only when allowed by condition of the linked substate one.