Implementation of a State Machine

Source: Internet
Author: User

Implementation of a State Machine

Interface IState {string Name {get; set;} // handle IList <IState> Nexts {get; set;} Func <IState/* this */, IState/* next */> Selector {get; set ;}} class State: IState {public string Name {get; set ;}= "State"; IList <IState> IState. nexts {get; set ;}= new List <IState> (); public Func <IState, IState> Selector {get; set ;}}

The status is relatively simple. It is identified by a Name, a list of subsequent parts, and a status selector.

For example, if status a can be transferred to status B, c, and d, the selector is one of them. As for how to choose, let the user define the actual selector.

Delegate bool HandleType <T> (IState current, IState previous, ref T value); interface IContext <T>: IEnumerator <T>, IEnumerable <T> {// data T Value {get; set;} // The IDictionary of the front-end processing <Tuple <IState/* this */, IState/* previous */>, handleType <T> Handles {get; set;} IState CurrentState {get; set;} bool transition (IState next );}

Different from the status class State, the Context class Context focuses on the status of the front part. When you jump to a new state, different policies must be implemented based on the current state. For example, if you want to enter status c, there are different processing programs based on the current status a, B, and d. This transfer handler corresponds to each other. Therefore, Tuple is used to describe a jump link in the <entry status, current status>. Then, bind the related handler with Dictionary.

The context will carry T Value data. How can this data be processed? I pass the ref parameter to the handler. Because I don't want IState to care about context construction, it only needs to care about the actual data T value;

The context stores the data and the current status, and then allows the user to control the transfer of the status through transiton. There is a duplicate here, because IState has a selector to control status transfer. Why? I want to construct a jump sequence. The IEnumerator and IEnumerable interfaces are introduced. However, the status can be automatically redirected under the selector, and then the result sequence can be read using foreach (except that it is useless ).

Class Context <T>: IContext <T> {T data; T IContext <T>. value {get => data; set => data = value;} IDictionary <Tuple <IState, IState>, HandleType <T> IContext <T>. handles {get; set ;}= new Dictionary <Tuple <IState, IState>, HandleType <T> (); public IState CurrentState {get; set ;} T IEnumerator <T>. current => (this as IContext <T> ). value; object IEnumerator. current => (this as IContext <T> ). value; bool I Context <T>. transition (IState next) {IContext <T> context = this as IContext <T>; if (context. currentState = null | context. currentState. nexts. contains (next) {// handle the front part var key = Tuple. create (next, context. currentState); if (context. handles. containsKey (key) & context. handles [key]! = Null) if (! Context. handles [key] (next, context. currentState, ref this. data) return false; context. currentState = next; return true;} return false;} bool IEnumerator. moveNext () {// IContext for post-processing <T> context = this as IContext <T>; IState current = context. currentState; if (current = null) throw new Exception ("the initial state must be set"); if (context. currentState. selector! = Null) {IState next = context. currentState. selector (context. currentState); return context. transition (next);} return false;} void IEnumerator. reset () {throw new NotImplementedException () ;}# region IDisposable Support private bool disposedValue = false; // call protected virtual void Dispose (bool disposing) {if (! DisposedValue) {if (disposing) {// TODO: Release the managed object ).} // TODO: Release unmanaged resources (unmanaged objects) and replace the terminator in the following content. // TODO: Set large fields to null. DisposedValue = true ;}}// TODO: The Terminator is replaced only when the above Dispose (bool disposing) has code for releasing unmanaged resources. //~ Context () {// do not change this code. Put the cleanup code in the above Dispose (bool disposing. // Dispose (false); // Add this code to implement the disposal mode correctly. Void IDisposable. Dispose () {// do not change this code. Put the cleanup code in the above Dispose (bool disposing. Dispose (true); // TODO: If the Terminator is replaced by the above content, uncomment the following line. // GC. suppressFinalize (this);} IEnumerator <T> IEnumerable <T>. getEnumerator () {return this;} IEnumerator IEnumerable. getEnumerator () {return this;} # endregion}

Focus on the transition function and MoveNext function.

Bool IContext <T>. transition (IState next) {IContext <T> context = this as IContext <T>; if (context. currentState = null | context. currentState. nexts. contains (next) {// handle the front part var key = Tuple. create (next, context. currentState); if (context. handles. containsKey (key) & context. handles [key]! = Null) if (! Context. Handles [key] (next, context. CurrentState, ref this. data) return false; context. CurrentState = next; return true;} return false ;}
 

It is also very easy to do, that is, to call the front processing program, the status will be transferred if the processing is successful, otherwise exit.

Bool IEnumerator. moveNext () {// IContext for post-processing <T> context = this as IContext <T>; IState current = context. currentState; if (current = null) throw new Exception ("the initial state must be set"); if (context. currentState. selector! = Null) {IState next = context. CurrentState. Selector (context. CurrentState); return context. transition (next);} return false ;}

MoveNext selects the next state through the selector.

In general, the implementation of my state machine is just a framework with no functions, but I feel it is easier to write the state transfer directory tree.

You must first create a group of statuses and then create a directory tree structure. My implementation is rough, because the user needs to build the directory tree, the front processor, and the back part selector separately. When I wrote the test code, I wrote nine states of the mesh structure, and the results were a bit dazzling. It would be better to unify them.

Note the construction of the first State and the final state. Otherwise, the system will not be able to stop or embed an endless loop.

// Test code // --------- create part --------- string mess = "";
// 3 IState s3 = new State () {Name = "s3"}; // 2 IState s2 = new State () {Name = "s2 "}; // 1 IState s1 = new State () {Name = "s1"}; // --------- combine --------- s1.Nexts = new List <IState> {s2, s3 }; s2.Nexts = new List <IState> {s1, s3}; s3.Nexts = new List <IState> {}; // note the end Writing Method // --------- Context --------- // transition IContext <int> cont = new Context <int> {CurrentState = s1}; // begin cont. value = 0 ;//- -------- Status processor --------- HandleType <int> funcLaji = (IState current, IState previous, ref int v) =>{ mess + = $ "{current. name}: junk {previous. name} \ n "; v ++; return true ;}; // 1 cont. handles. add (Tuple. create (s1, default (IState), funcLaji); cont. handles. add (Tuple. create (s1, s2), funcLaji); // 2 cont. handles. add (Tuple. create (s2, s1), funcLaji); // 3 cont. handles. add (Tuple. create (s3, s1), funcLaji); cont. H Andles. add (Tuple. create (s3, s2), funcLaji); // --------- status selector --------- var rval = new Random (); Func <int, int> round = x => rval. next (x); s1.Selector = st => round (2) = 0? S2: s3; s2.Selector = st => round (2) = 0? S1: s3;

After the construction, you can use this state machine.

// Selector jump to mess + = "selector jump: \ n ------------------------ \ n"; foreach (var stor in cont) mess + = $ "status change times: {stor} \ n "; // directly control the jump to mess + =" \ n directly control the status jump: \ n ---------------------- \ n "; cont. transition (s1); cont. transition (s2); cont. transition (s3 );

Here, thank you.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.