Table of Contents

State Machine Pattern API Reference

Complete API documentation for the State Machine pattern in PatternKit.

Namespace

using PatternKit.Behavioral.State;

StateMachine<TState, TEvent>

Fluent state machine with typed states, events, and transitions.

public sealed class StateMachine<TState, TEvent>
    where TState : notnull
    where TEvent : notnull

Type Parameters

Parameter Description
TState State type (typically enum)
TEvent Event/trigger type (typically enum)

Properties

Property Type Description
CurrentState TState Current state of the machine

Methods

Method Returns Description
Fire(TEvent) void Trigger event, transition if valid
TryFire(TEvent) bool Attempt trigger, returns success
CanFire(TEvent) bool Check if event is valid from current state
GetPermittedTriggers() IReadOnlyList<TEvent> List events valid from current state

Static Methods

Method Returns Description
Create(TState initial) Builder Creates builder with initial state

Exceptions

Method Exception Condition
Fire InvalidOperationException No valid transition for event

Example

var machine = StateMachine<OrderState, OrderEvent>.Create(OrderState.Pending)
    .State(OrderState.Pending)
        .On(OrderEvent.Pay).TransitionTo(OrderState.Paid)
        .On(OrderEvent.Cancel).TransitionTo(OrderState.Cancelled)
    .State(OrderState.Paid)
        .On(OrderEvent.Ship).TransitionTo(OrderState.Shipped)
    .Build();

machine.Fire(OrderEvent.Pay);
Console.WriteLine(machine.CurrentState); // Paid

if (machine.CanFire(OrderEvent.Ship))
    machine.Fire(OrderEvent.Ship);

StateMachine<TState, TEvent>.Builder

Fluent builder for configuring the state machine.

public sealed class Builder

Methods

Method Returns Description
State(TState) StateBuilder Configure a state
Build() StateMachine<TState, TEvent> Build immutable state machine

StateBuilder

Configuration for a specific state.

public sealed class StateBuilder

Methods

Method Returns Description
OnEntry(Action) StateBuilder Action when entering state
OnExit(Action) StateBuilder Action when leaving state
On(TEvent) TransitionBuilder Configure transition for event
State(TState) StateBuilder Move to configuring another state
Build() StateMachine<TState, TEvent> Build from state context

Example

.State(OrderState.Paid)
    .OnEntry(() => Console.WriteLine("Order paid!"))
    .OnExit(() => Console.WriteLine("Leaving paid state"))
    .On(OrderEvent.Ship).TransitionTo(OrderState.Shipped)

TransitionBuilder

Configuration for a specific transition.

public sealed class TransitionBuilder

Methods

Method Returns Description
TransitionTo(TState) StateBuilder Set target state
When(Func<bool>) TransitionBuilder Add guard condition
Execute(Action) TransitionBuilder Action during transition

Example

.State(OrderState.Paid)
    .On(OrderEvent.Ship)
        .When(() => inventory.IsAvailable())
        .Execute(() => inventory.Reserve())
        .TransitionTo(OrderState.Shipped)

Transition Types

Simple Transition

.On(Event.Trigger).TransitionTo(State.Target)

Guarded Transition

.On(Event.Trigger)
    .When(() => condition)
    .TransitionTo(State.Target)

Transition with Action

.On(Event.Trigger)
    .Execute(() => DoSomething())
    .TransitionTo(State.Target)

Internal Transition (no state change)

.On(Event.Trigger)
    .Execute(() => DoSomething())
    // No TransitionTo = internal transition

Self Transition (re-entry)

.State(State.Active)
    .On(Event.Refresh).TransitionTo(State.Active)
    // OnExit and OnEntry will fire

Execution Order

sequenceDiagram
    participant Client
    participant Machine
    participant Guard
    participant ExitAction
    participant TransitionAction
    participant EntryAction

    Client->>Machine: Fire(event)
    Machine->>Guard: Evaluate guard
    alt Guard passes
        Machine->>ExitAction: Execute OnExit
        Machine->>TransitionAction: Execute transition action
        Machine->>Machine: Update state
        Machine->>EntryAction: Execute OnEntry
    else Guard fails
        Machine-->>Client: Throw or return false
    end

Thread Safety

Component Thread-Safe
Builder No - use from single thread
StateMachine Partially - state reads safe, Fire needs sync
Fire No - external synchronization needed
CanFire Yes - read-only
GetPermittedTriggers Yes - read-only

Synchronizing State Changes

// Option 1: Lock externally
lock (lockObj)
{
    if (machine.CanFire(event))
        machine.Fire(event);
}

// Option 2: Use TryFire
if (!machine.TryFire(event))
    HandleInvalidTransition();

Complete Example

using PatternKit.Behavioral.State;

public enum DocumentState { Draft, Review, Approved, Published, Archived }
public enum DocumentEvent { Submit, Approve, Reject, Publish, Archive }

public class DocumentWorkflow
{
    private readonly StateMachine<DocumentState, DocumentEvent> _machine;

    public DocumentWorkflow()
    {
        _machine = StateMachine<DocumentState, DocumentEvent>.Create(DocumentState.Draft)
            .State(DocumentState.Draft)
                .OnEntry(() => Log("Entered draft"))
                .On(DocumentEvent.Submit).TransitionTo(DocumentState.Review)

            .State(DocumentState.Review)
                .On(DocumentEvent.Approve)
                    .When(() => HasApprovalAuthority())
                    .Execute(() => RecordApproval())
                    .TransitionTo(DocumentState.Approved)
                .On(DocumentEvent.Reject)
                    .Execute(() => NotifyAuthor())
                    .TransitionTo(DocumentState.Draft)

            .State(DocumentState.Approved)
                .On(DocumentEvent.Publish).TransitionTo(DocumentState.Published)

            .State(DocumentState.Published)
                .OnEntry(() => NotifySubscribers())
                .On(DocumentEvent.Archive).TransitionTo(DocumentState.Archived)

            .State(DocumentState.Archived)
                .OnEntry(() => Log("Document archived"))

            .Build();
    }

    public DocumentState State => _machine.CurrentState;

    public void Submit() => _machine.Fire(DocumentEvent.Submit);
    public void Approve() => _machine.Fire(DocumentEvent.Approve);
    public void Reject() => _machine.Fire(DocumentEvent.Reject);
    public void Publish() => _machine.Fire(DocumentEvent.Publish);
    public void Archive() => _machine.Fire(DocumentEvent.Archive);

    public IReadOnlyList<DocumentEvent> AvailableActions =>
        _machine.GetPermittedTriggers();
}

// Usage
var workflow = new DocumentWorkflow();
workflow.Submit();   // Draft -> Review
workflow.Approve();  // Review -> Approved
workflow.Publish();  // Approved -> Published

See Also