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:
Fluent interface