2013-04-09 22 views
5

Ho scritto un semplice FSM dinamico. Dynamic significa che le transizioni di stato sono dinamiche e non statiche come mostrato in ConcreteStateB.Altro approccio .net per macchina a stati dinamici

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

Questo implementa una macchina a stati come descritto in GoF305.

Da quando sono nuovo a C# e .net: ci sono approcci migliori per raggiungere questo obiettivo utilizzando funzionalità più specifiche da .net o C#?

risposta

5

risposta di Outcoldman fornisce numerose grandi opzioni.

Ora, so che il codice qui sotto non è un corretto FSM secondo lo schema, ma per implementazioni molto semplici potrebbe aiutarti a evitare la scrittura di molte sottoclassi extra. È solo questione di decidere lo strumento giusto per il lavoro. Questo si concentra principalmente intorno all'uso del delegato Action<T> generico:

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

E avere la "macchina dello Stato", come:

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

E per dando il via al processo che si sarebbe usare:

Inoltre, per gli stati non correlati è possibile utilizzare anche espressioni Lambda, ad esempio:

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

Ci sono molti approcci che è possibile applicare, ma in gran parte dipende dal compito che è necessario raggiungere.

  1. È possibile utilizzare l'interfaccia anziché la classe astratta. In C# non puoi ereditare più di una classe, quindi è sempre bene non prendere questa opzione dalla realizzazione.

    interface IState 
    { 
        void Handle(Context context); 
    } 
    
  2. È possibile utilizzare farmaci generici, in modo da poter scrivere le interfacce di base/classi per modello di Stato, una volta e usarlo ovunque:

    abstract class IState<T> 
    { 
        void Handle(T context); 
    } 
    
  3. cose Avanti dipendono da che cosa si vuole nascondere o don voglio nascondermi Ad esempio, puoi probabilmente nascondere il setter per lo stato di proprietà, per assicurarti che nessuno possa usarlo al di fuori della tua DLL, in modo che tu possa impostare il setter di questa proprietà internal.

  4. È possibile utilizzare asincrona per Stato Cambiamento, qualcosa come

    interface IState 
    { 
        Task HandleAsync(Context context); 
    } 
    
    class Context 
    { 
        // ... 
    
        public async Task RunAsync() 
        { 
         while (_state != null) 
         { 
          await _state.HandleAsync(this); 
         } 
        } 
    } 
    
  5. La mia scommessa che qualcuno già implementato con Rx