Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

More .net approach for dynamic state machine

I wrote a simple dynamic FSM. Dynamic means the state transitions are dynamic and not static as shown in ConcreteStateB.

namespace FSM_Example
{
    using System;

    class Program
    {
        static void Main()
        {
            var context = new Context(new ConcreteStateA());
            context.Run();
            Console.Read();
        }
    }

    abstract class State
    {
        public abstract void Execute(Context context);
    }

    class ConcreteStateA : State
    {
        public override void Execute(Context context)
        {
            context.State = new ConcreteStateB();
        }
    }

    class ConcreteStateB : State
    {
        public override void Execute(Context context)
        {
            Console.Write("Input state: ");
            string input = Console.ReadLine();
            context.State = input == "e" ? null : new ConcreteStateA();
        }
    }

    class Context
    {
        private State _state;

        public Context(State state)
        {
            State = state;
        }

        public State State
        {
            get { return _state; }
            set
            {
                _state = value;
                Console.WriteLine("State: " + _state.GetType().Name);
            }
        }

        public void Run()
        {
            while (_state != null)
            {
                _state.Execute(this);
            }
        }
    }
}

This implements a state machine as described in GoF305.

Since I'm new to C# and .net: Are there better approaches archieving this goal using more specific features from .net or C#?

like image 645
Razer Avatar asked Apr 09 '13 21:04

Razer


2 Answers

Outcoldman's answer provides numerous great options.

Now, I know that the code below is not a proper FSM according to the pattern, but for very simple implementations it could help you avoid writing a lot of extra subclasses. It's just a matter of deciding the right tool for the job. This one mainly focuses around the use of the Action<T> generic delegate:

public class Context
{
    public Action<Context> State { get; internal set; }

    public Context(Action<Context> state)
    {
        State = state;
    }

    public void Run()
    {
        while (State != null)
        {
            State(this);
        }
    }
}

And have the "state machine" as:

public static class SimpleStateMachine
{
    public static void StateA(Context context)
    {
        context.State = StateB;
    }
    public static void StateB(Context context)
    {
        Console.Write("Input state: ");
        var input = Console.ReadLine();
        context.State = input == "e" ? (Action<Context>)null : StateA;
    }
}

And for kicking off the process you'd use:

var context = new Context(SimpleStateMachine.StateA);
context.Run();
Console.Read();

Also, for states that aren't related you can use Lambda expressions as well, such as:

Action<Context> process = context =>
    {
        //do something
        context.State = nextContext =>
            {
                //something else
                nextContext.State = null;
            };
    };
like image 160
Pablo Romeo Avatar answered Oct 11 '22 18:10

Pablo Romeo


There are plenty approaches which you can apply, but mostly it depends on the task which you need to achieve.

  1. You can use interface instead of abstract class. In C# you cannot inherit more than one class, so it is always good to not take this option from realization.

    interface IState
    {
         void Handle(Context context);
    }
    
  2. You can use generics, so you can write base interfaces / classes for State pattern once and use it everywhere:

    abstract class IState<T>
    {
         void Handle(T context);
    }
    
  3. Next things depend on what do you want to hide or don't want to hide. For example you can probably hide setter for property State, to make sure that nobody can use outside of your dll, so you can make the setter of this property internal.

  4. You can use Async for State Change, something like

    interface IState
    {
         Task HandleAsync(Context context);
    }
    
    class Context
    {
        // ...
    
        public async Task RunAsync()
        {
            while (_state != null)
            {
                await _state.HandleAsync(this);
            }
        }
    }
    
  5. My bet that somebody already implemented it with Rx

like image 26
outcoldman Avatar answered Oct 11 '22 18:10

outcoldman