SW Design Pattern

Pattern state in practice


La storia dei design pattern inizia nel 1994 con la pubblicazione del libro Design Patterns: Elements of Reusable Object-Oriented Software.I 4 autori Erich Gamma, Richard Helm, Ralph Johnson e John Vlissides (The gang of four) possono essere considerati dei mostri sacri nella progettazione object oriented.I design pattern rappresentano le risposte alle principali esigenze di programmazione in un ambiente Object Oriented. In pratica rappresentano le risposte ad alcune problematiche tipiche che si incontrano nelle progettazione di qualsiasi tipo di applicazione.In questo primo articolo riporto un Patter molto semplice denominato State. Nella programmazione orientata agli oggetti, lo State è un design pattern comportamentale. Esso consente ad un oggetto di cambiare il proprio comportamento a run-time in funzione dello stato in cui si trova.In questo articolo utilizzo il pattern per modellare una macchina a stati. L'impiego di macchine a stati è comune nel mondo dell'automazione per il controllo di passi di lavorazione che trasformano un insieme di materiali in un prodotto finito.Ci sono diversi elementi che agiscono per modellare una macchina a stati. C'è un contesto di esecuzione, uno stato corrente e degli atri stati raggiungibili. Infine degli eventi che comportano delle transizioni da uno stato verso un altro. Il pattern da proprio questa definizione definendo diverse classi:ContextModella il contesto di esecuzione della macchina a stati, contiene uno stato corrente e fornisce il punto di accessso unico per tutte le richieste di cambio di stato. Uno dei punti di forza del pattern è che dall'esterno non c'è alcun tipo di percezione dello stato in cui la macchina è attualmente. Si possono solamente comandare delle transizioni che il contesto può assorbire o meno. Le transizioni provengono tramite il metodo ChangeStateRequest(StateTrasitionEnum.DEST).Il contesto deve contenere tutte le informazioni utili alla logica interna per rendere autonoma la macchina a stati ad un eventuale il cambio di stato. Memorizzo nel contesto la richiesta proveniente dall'esterno per fare in modo che il current State possa averne conoscienza. Inoltre posso in questo modo debuggare l'ultima richiesta che la macchina ha avuto.StateUna classe astratta che contiene un metodo di gestione dello stato Handle. Da questa classe erediteranno tutti gli stati reali che il contesto può assumere.InitialStateLo stato iniziale della macchina a stati. Prevede un funzionamento particolare visto che è il primo stato dove la macchina a stati andrà a eseguire. Questo stato prevede la transizione in altri stati solo se un bool stateMachineIsEnabled è abilitato. E' infatti importante in certi casi disabilitare la macchina a stati alle transizioni per esempio quando si incontrino casi di errore.X_State, Y_StateDue stati di lavoro nei quali la macchina può saltare. Nell'esempio si salta in modo alternato ed incondizionato dallo stato X_State, Y_State. C' è un controllo sul comando di transizione per fare in modo che la macchina transiti dallo X_State verso lo Y_State solo se c'è stato un comando di tipo StateTransitionEnum.TO_STATE_Y. In questa specifica implementazione comandi di transizione consecutivi identici (2 StateTransitionEnum.TO_STATE_Y.) comporteranno un unico cambio di stato ma questa è una mia interpretazione che può essere cambiata a seconda delle varie esigenze. Nello X_State ho implementato per completezza la funzione DoSomethingOnTransaction(Context context) per implementare qualche azione durante la transazione di stato.Implementazione in C# .NETusing System;using System.Collections.Generic;using System.Linq;using System.Text;namespace PatternState{    class Program    {        static void Main(string[] args)        {            bool stateMachineEnabled = true;            Context myContext = new Context(stateMachineEnabled);            myContext.ChangeStateRequest(StateTransitionEnum.TO_STATE_X);            myContext.ChangeStateRequest(StateTransitionEnum.TO_STATE_Y);            Console.ReadKey();        }    }    public enum StateTransitionEnum    {        TO_STATE_X = 0,        TO_STATE_Y = 1,    }    public class Context    {        private State currentState;        private bool stateMachineEnabled;        private StateTransitionEnum stateTransitionEnum;        public State CurrentState        {            get            {                return this.currentState;            }            set            {                this.currentState = value;#if DEBUG                Console.WriteLine("CurrentState: " + currentState.GetType().Name);#endif            }        }        public bool StateMachineEnabled        {            get { return stateMachineEnabled; }        }        public StateTransitionEnum StateTransitionEnum        {            get { return stateTransitionEnum; }        }        public Context(bool stateMachineEnabled)        {            this.stateMachineEnabled = stateMachineEnabled;            this.CurrentState = new InitialState();        }        public void ChangeStateRequest(StateTransitionEnum stateTransitionEnum)        {            this.stateTransitionEnum = stateTransitionEnum;            this.Request();        }        private void Request()        {            currentState.Handle(this);        }    }    public abstract class State    {        public abstract void Handle(Context messageSwitchContext);    }    public class InitialState : State    {        public override void Handle(Context context)        {            if (context.StateMachineEnabled)            {                switch (context.StateTransitionEnum)                {                    case StateTransitionEnum.TO_STATE_Y:                        context.CurrentState = new Y_State();                        break;                    case StateTransitionEnum.TO_STATE_X:                        context.CurrentState = new X_State();                        break;                    default:                        context.CurrentState = new InitialState();                        break;                }            }        }    }    public class X_State : State    {        public override void Handle(Context context)        {            if (context.StateTransitionEnum == StateTransitionEnum.TO_STATE_Y)            {                //I'm switching to state Y                this.DoSomethingOnTransaction(context);                context.CurrentState = new Y_State();            }        }        private void DoSomethingOnTransaction(Context context)        {            Console.WriteLine("Transaction to " + context.CurrentState.GetType().Name);        }    }    public class Y_State : State    {        public override void Handle(Context messageSwitchContext)        {            if (messageSwitchContext.StateTransitionEnum == StateTransitionEnum.TO_STATE_X)            {                //I'm switching to state X                messageSwitchContext.CurrentState = new X_State();            }        }    }}