Simple State Machine Updates
Introduction
Some of you may remember my SimpleStateMachine project; I blogged about it here and here. It is available on GitHub and Nuget.
I made a few changes to it:
- An alternative way to configure states
- Adding state to states (pun intended)
- Converting attributes to code state machines
- The possibility to clear state transitions (unsure about this one, but, it's here, at least for now)
Let's see how these work.
Building a State Machine
We used to have two ways for building a simple state machine:
- From attributes
- From code
I'm going to introduce a new one, but, first, here is quick recap for the existing methods.
From Attributes
As simple as adding attributes to enumeration fields:
public enum TicketState
{
[InitialState]
[Transitions<TicketState>(TicketState.Ready, TicketState.Closed)]
Created,
[Transitions<TicketState>(TicketState.InProgress, TicketState.Blocked, TicketState.Closed)]
Ready,
[Transitions<TicketState>(TicketState.Blocked, TicketState.InReview, TicketState.Closed, TicketState.Ready)]
InProgress, [Transitions<TicketState>(TicketState.InProgress, TicketState.Closed)]
Blocked,
[Transitions<TicketState>(TicketState.InProgress, TicketState.Closed)]
InReview,
Closed
}
From Code
Also simple to understand:
var created = TicketState.Created;
var stateMachine = created.Create();
stateMachine.CanTransitionTo(created, TicketState.Ready, TicketState.Closed);
stateMachine.CanTransitionTo(TicketState.Ready, TicketState.Blocked, TicketState.InReview, TicketState.Closed, TicketState.Ready);
stateMachine.CanTransitionTo(TicketState.InProgress, TicketState.InProgress, TicketState.Closed);
stateMachine.CanTransitionTo(TicketState.Blocked, TicketState.InProgress, TicketState.Closed);
stateMachine.CanTransitionTo(TicketState.InReview, TicketState.InProgress, TicketState.Closed);
From Loquacious Code
This is the new one, I called it loquacious for lack of a better name. We start from an existing state machine and then configure the state transitions individually:
var created = TicketState.Created;
var stateMachine = created.Create();
stateMachine
.From(created)
.To(TicketState.Ready, TicketState.Closed);
stateMachine
.From(TicketState.Ready)
.To(TicketState.InProgress, TicketState.Blocked, TicketState.Closed);
stateMachine
.From(TicketState.InProgress)
.To(TicketState.Blocked, TicketState.InReview, TicketState.Closed, TicketState.Ready);
stateMachine
.From(TicketState.Blocked)
.To(TicketState.InProgress, TicketState.Closed);
stateMachine
.From(TicketState.InReview)
.To(TicketState.InProgress, TicketState.Closed);
There is now an extension method From that can be used to add or append state transitions (To) to an existing state.
The three ways are perfectly analogous, the last two use an IStateMachine<T> instance while the first only uses attributes and extension methods over them.
Adding State to a State
It is now possible to add arbitrary state content to some state machine state. This can be, for example, some string or some number. All three ways support this.
Adding State Using Attributes
Let's introduce the new [State] attribute:
public enum TicketState
{
[InitialState]
[State("Initial State")]
Created,
//rest goes here...
}
The [State] attribute can be applied at most once to any enumeration field to add custom state. It is possible to retrieve the custom state for an enumeration field using the GetState extension method:
var initialState = TicketState.Created;
var state = initialState.GetState(); //returns "Initial State"
If no state was provided, GetState returns null.
Adding State Using Code
Similar using code, we have the SetState method on IStateMachine<T>:
var created = TicketState.Created;
var stateMachine = created.Create();
stateMachine.SetState(created, "Initial State");
And GetState too:
stateMachine.GetState(created); //returns "Initial State"
Adding State Using Loquacious Code
As for the new loquacious approach, the new To method can receive an optional state parameter:
stateMachine
.From(created, "Initial State")
.To(TicketState.Ready, TicketState.Closed);
GetState can be used to retrive the custom state, as in the previous example:
stateMachine.GetState(created); //returns "Initial State"
Converting Attributes to Code
It is also now possible to convert an attributes-based state machine to a code one (IStateMachine<T>). For that we use the Create method over an enumeration type:
var created = TicketState.Created;
var stateMachine = StateMachineExtensions.Create<TicketState>();
The returned IStateMachine<T> can be used as if it had been created normally.
Clearing Transitions
Now, if you pass an empty array (not null, mind you) to CanTransitionTo or To methods, the state transitions are cleared. Again, I'm still to see if this is useful or not, looking forward to hearing you on this.
Conclusion
As always, hope you find this useful, and I'm super interested in hearing your thoughts. The Nuget package hasn't been updated yet, but will be shortly. Do give it a try and let me hear from your!
GitHub: http://github.com/rjperes/SimpleStateMachine
Nuget: https://nuget.org/packages/SimpleStateMachine
Comments
Post a Comment