State Machine
Motivation
Our applications are full of state machines. Enabled and disabled UI elements, abstractions of devices and business logic. Implementing these state machines with the state pattern is overly complicated. Therefore, we implemented a state machine component that allows implementing a state machine as a single class. This reduces complexity and needed effort dramatically. See for yourself.
Highlights
async/await support: from version 4.0.0 on, the state machine supports async/await
.net standard: from version 4.0.0 on, the state machine targets .net standard 1.3
Features
- use enums, ints, strings or your own class for states and events - resulting in single class state machines.
- transition, entry and exit actions.
- transition guards
- hierarchical states with history behavior to initialize state always to same state or last active state.
- supports async/await
- fluent definition syntax.
- passive state machine handles state transitions synchronously.
- active state machine handles state transitions asynchronously on the worker thread of the state machine.
- active state machine is thread-safe
- current state and history states can be persisted
- extension support to extend functionality of state machine.
- extensible thorough logging simplifies debugging.
- state machine reports as text, csv or yEd diagram. Or write your own report.
For a quick introduction see the Tutorial.
For the specification see Specification
StateMachine
Appccelerate contains two different state machines:
All these state machines implement the interface IStateMachine. For better testability and flexibility, I suggest that you reference your state machine only by the interface and use dependency injection to get either of the implementations. You can use then a passive state machine as a replacement for an active one in your tests to simplify them because everything is run on the same thread.States and Events
A state machine is defined using States and Events. States and events have to be IComparables (enum, int, string, ...). If you have a well known set of states and events they I suggest you use enums which make the code much better readable. If you plan to build some flexibility into your state machine (e.g. add states, transitions in base classes) then you better use an "open" type like string, integer.
Transitions
Transitions are state switches that are executed in response of an event that was fired onto the state machine. You can define per state and event which transition is taken and therefore which state to go to.
Actions
You can define actions either on transitions or on entry or exit of a state. Transition actions are executed when the transition is taken as response to an event. The entry and exit action of a state are excuted when the state machine enters or exits the state due to a taken transition. In case of hierarchical states, multiple entry and exit actions can be executed.
Guards
Guards give you the possibility to decide which transition is executed depending on a boolean criteria. When an event is fired onto the state machine, it takes all transitions defined in the current state for the fired event and executes the first transition with a guard returning true.
Extensions
Extensions can be used to extend the functionality of the state machine. They provide for example a simple way to write loggers.
Reports
Out of the box, Appccelerate provides a textual report, a csv report and a yEd diagram reporter. You can add your own reports by just implementing IStateMachineReport.