2013-04-09 37 views
5

Tôi đã viết một FSM động đơn giản. Dynamic có nghĩa là chuyển đổi trạng thái là động và không tĩnh như được hiển thị trong ConcreteStateB.Cách tiếp cận khác .net cho máy trạng thái động

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

Điều này thực hiện máy trạng thái như được mô tả trong GoF305.

Vì tôi mới sử dụng C# và .net: Có phương pháp nào tốt hơn để lưu mục tiêu này bằng các tính năng cụ thể hơn từ .net hoặc C#?

Trả lời

5

câu trả lời Outcoldman của cung cấp nhiều lựa chọn tuyệt vời. Bây giờ, tôi biết rằng mã bên dưới không phải là FSM phù hợp theo mẫu, nhưng đối với việc triển khai rất đơn giản, nó có thể giúp bạn tránh viết nhiều lớp con phụ. Nó chỉ là vấn đề quyết định đúng công cụ cho công việc. Cái này chủ yếu tập trung xung quanh việc sử dụng các đại biểu Action<T> generic:

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

Và có "máy nhà nước" như:

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

Và khi đá ra khỏi quá trình bạn muốn sử dụng:

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

Ngoài ra, đối với các tiểu bang không có liên quan, bạn cũng có thể sử dụng biểu thức Lambda, chẳng hạn như:

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

Có nhiều cách tiếp cận mà bạn có thể áp dụng, nhưng phần lớn nó phụ thuộc vào nhiệm vụ bạn cần đạt được.

  1. Bạn có thể sử dụng giao diện thay vì lớp trừu tượng. Trong C# bạn không thể kế thừa nhiều hơn một lớp, vì vậy nó luôn luôn là tốt để không có tùy chọn này từ thực hiện.

    interface IState 
    { 
        void Handle(Context context); 
    } 
    
  2. Bạn có thể sử dụng Generics, vì vậy bạn có thể viết các giao diện cơ sở/lớp dành cho mô hình Nhà nước một lần và sử dụng nó ở khắp mọi nơi:

    abstract class IState<T> 
    { 
        void Handle(T context); 
    } 
    
  3. điều kế tiếp phụ thuộc vào những gì bạn muốn ẩn hoặc don không muốn trốn. Ví dụ bạn có thể ẩn setter cho nhà nước tài sản, để đảm bảo rằng không ai có thể sử dụng bên ngoài của dll của bạn, vì vậy bạn có thể làm cho setter của tài sản này internal.

  4. Bạn có thể sử dụng Async cho Nhà nước thay đổi, giống như

    interface IState 
    { 
        Task HandleAsync(Context context); 
    } 
    
    class Context 
    { 
        // ... 
    
        public async Task RunAsync() 
        { 
         while (_state != null) 
         { 
          await _state.HandleAsync(this); 
         } 
        } 
    } 
    
  5. đặt cược của tôi rằng ai đó đã thực hiện nó với Rx

Các vấn đề liên quan