Macchina a stati usando una fluent interface

Qualche giorno fa Antonio ha postato un interessante esempio di come sia possibile descrivere una macchina a stati usando C# ed un’approccio FluentInterface.
L’esempio mi sembrava interessa ho provato riscrivere la mia versione dello stesso codice. 
I commenti del post non mi sembravano il posto migliore (avrei perso la formattazione) quindi ho deciso di postare qui la mia soluzione:

Innanzi tutto ho modificato la descrizione della macchina così

return StateMachine.describedAs .hasState( State.called("EntryNotSubmitted") .doAction(trade => broker.PlaceOrder(trade)) .onSuccessGoTo("EntrySubmitted") .onExceptionGoTo("EntryError") ) .hasState( State.called("EntrySubmitted") .doAction(trade => broker.IsExpired(trade)) .onReturnValueGoTo(true, "EntryNotExecuted") ) .hasState(State.called("EntryError")) .hasState( State.called("EntryNotExecuted") .doAction(trade => broker.GetOrderStatus(trade)) .onReturnValueGoTo(OrderStatus.Submitted, "EntrySubmitted") .onReturnValueGoTo(OrderStatus.Executed, "ExitNotSubmitted") .onExceptionGoTo("EntryError") );

 

poi ho ridotto il numero di oggetti necessari alla descrizione della mia macchina a stati.
Adesso ho un’oggetto StateMachine che rappresenta la struttura completa

public class StateMachine { private List<State> _list; public static StateMachine describedAs { get { return new StateMachine(); } } public StateMachine hasState(State state) { _list.Add(state); return this; } public void Start(Trade trade) { string nextState; State current = _list[0]; do { nextState = current.Execute(trade); current = GetState(nextState); } while (!string.IsNullOrEmpty(nextState)); } private State GetState(IEquatable<string> stateName) { return _list.Find(state => stateName.Equals(state.Name)); } }

ed un’oggetto State per ogni stato.

public class State { private Func<Trade, object> _doAction; private string _onExceptionGoTo; private string _onSuccessGoTo; public string Name { get; set; } private readonly Dictionary<object, string> _states = new Dictionary<object, string>(); private State(string name) { Name = name; } public static State called(string name) { return new State(name); } public State doAction(Func<Trade, object> action) { _doAction = action; return this; } public State onSuccessGoTo(string s) { _onSuccessGoTo = s; return this; } public State onExceptionGoTo(string s) { _onExceptionGoTo = s; return this; } public State onReturnValueGoTo(object key, string value) { _states.Add(key, value); return this; } public string Execute(Trade trade) { try { object returnValue = _doAction(trade); if (!string.IsNullOrEmpty(_onSuccessGoTo)) return _onSuccessGoTo; if (_states.ContainsKey(returnValue)) return _states[returnValue]; return string.Empty; } catch (Exception) { if (string.IsNullOrEmpty(_onExceptionGoTo)) throw; return _onExceptionGoTo; } } }

non avendo a disposizione alcun test non ho potuto verificarne il corretto funzionamento ma mi sembra che la soluzione possa stare in piedi.

I commenti sono aperti e ben accetti!

Technorati Tag:

posted @ mercoledì 14 gennaio 2009 10:00

Print

Comments on this entry:

# re: Macchina a stati usando una fluent interface

Left by Antonio Ganci at 14/01/2009 13:53
Gravatar
Trovo i nomi dei metodi:
- describedAs
- hasState
fuorvianti li chiamerei:
- newStateMachine
- addState oppure State

Inoltre la classe StateMachine è difficile da testare, ed è lo stesso problema che ho avuto con la prima versione che avevo scritto.
Inizialmente non avevo il StateMachineConfigurator, ma solo la StateMachine e questo mi costringeva a fare la new dentro la classe e quindi rendeva il codice meno testabile.
Se provi a scrivere i test di accorgerai del problema.
Comments have been closed on this topic.