csharp-stateless

Crea máquinas de estado en C# con Stateless .NET

La biblioteca Stateless es una herramienta de código abierto para .NET que nos permite crear máquinas de estado y flujos de trabajo de manera sencilla.

Las máquinas de estado son una metodología de modelado que resulta adecuada para la resolución de ciertos tipos de problemas.

Stateless proporciona todas las funcionalidades que habitualmente necesitamos para generar máquinas de estado.

  • Estados y disparadores genéricos
  • Jerarquía de estados
  • Acciones al entrar o salir de estados
  • Transiciones

Además de otras muchas no “clásicas”, pero que pueden resultar de utilidad en ciertos casos. La verdad que, en este sentido, es una librería muy muy completa.

Cómo usar Stateless .NET

Para instalar “Stateless” en un proyecto de .NET, se puede utilizar el administrador de paquetes NuGet. Abra la consola del administrador de paquetes de NuGet y ejecute el comando

Install-Package Stateless

Aquí tenemos un ejemplo de código, extraído de la propia documentación de la librería, que simplemente simula un control ON/OFF

const string on = "On";
const string off = "Off";
const char space = ' ';

// Instantiate a new state machine in the 'off' state
var onOffSwitch = new StateMachine<string, char>(off);

onOffSwitch.Configure(off).Permit(space, on);
onOffSwitch.Configure(on).Permit(space, off);

Console.WriteLine("Press <space> to toggle the switch. Any other key will exit the program.");

while (true)
{
	Console.WriteLine("Switch is in state: " + onOffSwitch.State);
	var pressed = Console.ReadKey(true).KeyChar;
	
	// Check if user wants to exit
	if (pressed != space) break;

	// Use the Fire method with the trigger as payload to supply the state machine with an event.
	// The state machine will react according to its configuration.
	onOffSwitch.Fire(pressed);
}

En este caso tenemos una maquina de estados, en el que el estado viene identificado por un string, y el disparador por un char.

Únicamente tiene dos estados, “On” y “Off”, y un disparador, ’ ’ (barra espaciadora).

Se emplea la función ‘configure’ para definir las transiciones del estado On a Off, y viceversa. Ambos, ante el disparador ’ ‘.

Finalmente se usa un bucle while infinito, se comprueba la entrada de teclado, y en caso de ser un espacio se dispara la transición con el comando Fire.

Este es un ejemplo muy sencillo y mínimo para mostrar el funcionamiento. Por ejemplo, uno má s complejo sería la simulación del estado de una herramienta de tracking de bugs

private enum State { Open, Assigned, Deferred, Closed }

private enum Trigger { Assign, Defer, Close }

private readonly StateMachine<State, Trigger> _machine = new StateMachine<State, Trigger>(State.Open);

// ...

// Instantiate a new trigger with a parameter. 
_assignTrigger = _machine.SetTriggerParameters<string>(Trigger.Assign);

// Configure the Open state
_machine.Configure(State.Open)
	.Permit(Trigger.Assign, State.Assigned);

// Configure the Assigned state
_machine.Configure(State.Assigned)
	.SubstateOf(State.Open)
	.OnEntryFrom(_assignTrigger, OnAssigned)  // This is where the TriggerWithParameters is used. Note that the TriggerWithParameters object is used, not something from the enum
	.PermitReentry(Trigger.Assign)
	.Permit(Trigger.Close, State.Closed)
	.Permit(Trigger.Defer, State.Deferred)
	.OnExit(OnDeassigned);

// Configure the Deferred state
_machine.Configure(State.Deferred)
	.OnEntry(() => _assignee = null)
	.Permit(Trigger.Assign, State.Assigned);

En este caso, estamos usando enumeraciones tanto para el estado como para disparador, que será la forma normal de trabajar con la librería.

A continuación se definen las transiciones entre estados, y las acciones a realizar al entrar y salir de los nodos.

Como vemos, es una librería muy completa para definir máquinas de estado. Seguro que tiene todas las funcionalidades que alguna vez podáis necesitar en vuestro proyecto. Para más información, podéis consultar la documentación y ejemplos de la librería.