2013-04-09 3 views
5

Я написал простой динамический FSM. Dynamic означает, что переход состояния является динамическим, а не статическим, как показано в ConcreteStateB.Подробнее .net-подход для динамического конечного автомата

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); 
      } 
     } 
    } 
} 

Это реализует конечный автомат, как описано в GoF305.

Поскольку я новичок в C# и .net: Есть ли более эффективные способы архивирования этой цели, используя более конкретные функции от .net или C#?

ответ

5

ответ Outcoldman предоставляет многочисленные большие варианты.

Теперь я знаю, что приведенный ниже код не является надлежащим FSM в соответствии с шаблоном, но для очень простых реализаций он может помочь вам избежать написания большого количества дополнительных подклассов. Это просто вопрос выбора правильного инструмента для работы. Это одна в основном сосредоточена вокруг использования Action<T> общего делегата:

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); 
     } 
    } 
} 

И есть «государственную машину», как:

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; 
    } 
} 

И сбрасывая процесс вы будете использовать:

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

Кроме того, для состояний, которые не связаны, вы также можете использовать выражения Лямбда, такие как:

Action<Context> process = context => 
    { 
     //do something 
     context.State = nextContext => 
      { 
       //something else 
       nextContext.State = null; 
      }; 
    }; 
2

Существует множество подходов, которые вы можете применить, но в основном это зависит от задачи, которую вам нужно достичь.

  1. Вы можете использовать интерфейс вместо абстрактного класса. В C# вы не можете наследовать более одного класса, поэтому всегда полезно не принимать этот вариант от реализации.

    interface IState 
    { 
        void Handle(Context context); 
    } 
    
  2. Вы можете использовать дженерики, так что вы можете писать базовые интерфейсы/классы для государственного образца один раз и использовать его везде:

    abstract class IState<T> 
    { 
        void Handle(T context); 
    } 
    
  3. Следующая многое зависит от того, что вы хотите, чтобы скрыть или дон Не хочу спрятаться. Например, вы можете спрятать setter для свойства State, чтобы убедиться, что никто не может использовать вне вашей DLL, поэтому вы можете сделать установщик этого свойства internal.

  4. Вы можете использовать Async для государственного изменения, что-то вроде

    interface IState 
    { 
        Task HandleAsync(Context context); 
    } 
    
    class Context 
    { 
        // ... 
    
        public async Task RunAsync() 
        { 
         while (_state != null) 
         { 
          await _state.HandleAsync(this); 
         } 
        } 
    } 
    
  5. Моего пари, что кто-то уже реализованного его Rx

Смежные вопросы