I am new to the state pattern implementation in C#, could you provide some info on how you implement it.
I am refactoring a state machine in C# using the state pattern. Currently my state machine contains 5 states and it is only possible to go forward or backward througout the states,i.e. from state 1 you need to go to state 2, 3 and 4 to finally arrive to state 5.
I am able to go forward just doing
mainclass.State = new NextSate();
which creates a new state every time you want to go forward, however, once all of them have been created and/or you want to go backward I would need to go to the same states, not just a new one. How can I do that? Is there any better way to do it simple?
State is a behavioral design pattern that allows an object to change the behavior when its internal state changes. The pattern extracts state-related behaviors into separate state classes and forces the original object to delegate the work to an instance of these classes, instead of acting on its own.
Example. The State pattern allows an object to change its behavior when its internal state changes. This pattern can be observed in a vending machine. Vending machines have states based on the inventory, amount of currency deposited, the ability to make change, the item selected, etc.
Design patterns are solutions to software design problems you find again and again in real-world application development. Patterns are about reusable designs and interactions of objects. The 23 Gang of Four (GoF) patterns are generally considered the foundation for all other patterns.
All functions in C always use the same basic pattern (syntax). This syntax can be captured in a "Design Pattern".
Strictly speaking, if you're implementing the classic GoF State pattern then the State subclasses themselves are responsible for knowing about and performing the State transitions. The holder of the State isn't responsible for managing the transitions and a large part of the intent of the pattern is to encapsulate the state transition behaviour in the State objects and thus for the client to delegate to them. I've introduced a Factory that ensures that there is only ever a single instance of each State subclass to ensure that the same instance is reused when moving back and forth through the states.
public abstract class State
{
protected StateFactory _factory;
protected IStateUser _context;
public State(StateFactory factory, IStateUser context)
{
_factory = factory;
_context = context;
}
protected void TransitionTo<T>(Func<T> creator) where T : State
{
State state = _factory.GetOrCreate<T>(creator);
_context.CurrentState = state;
}
public abstract void MoveNext();
public abstract void MovePrevious();
}
public class State1 : State
{
public State1(StateFactory factory, IStateUser context)
: base(factory, context)
{
}
public override void MoveNext()
{
TransitionTo<State2>(() => new State2(_factory, _context));
}
public override void MovePrevious()
{
throw new InvalidOperationException();
}
}
public class State2 : State
{
public State2(StateFactory factory, IStateUser context)
: base(factory, context)
{
}
public override void MoveNext()
{
TransitionTo<State3>(() => new State3(_factory, _context)); //State 3 is omitted for brevity
}
public override void MovePrevious()
{
TransitionTo<State1>(() => new State1(_factory, _context));
}
}
public interface IStateUser
{
State CurrentState { get; set; }
}
public class Client : IStateUser
{
public Client()
{
var factory = new StateFactory();
var first = new State1(factory, this);
CurrentState = factory.GetOrCreate<State1>(() => first);
}
public void MethodThatCausesTransitionToNextState()
{
CurrentState.MoveNext();
}
public void MethodThatCausesTransitionToPreviousState()
{
CurrentState.MovePrevious();
}
public State CurrentState
{
get;
set;
}
}
public class StateFactory
{
private Dictionary<string, State> _states = new Dictionary<string, State>();
public State GetOrCreate<T>(Func<T> creator) where T : State
{
string typeName = typeof(T).FullName;
if (_states.ContainsKey(typeName))
return _states[typeName];
T state = creator();
_states.Add(typeName, state);
return state;
}
}
Use internal stack to maintain the previous states:
public class MyClass
{
private Stack<State> _states;
private State _currentState;
public void GoToNextState()
{
// If Not last state then
_states.Push(_currentState);
_currentState = new NextState();
}
public void GoToPrevState()
{
// if not the first state
_currentState = _states.Pop();
}
}
if you want to maintain forward and backward states then create additional stack:
public class MyClass
{
private readonly Stack<State> _nextStates = new Stack<State>();
private readonly Stack<State> _prevStates = new Stack<State>();
private State _currentState = new SampleState1();
public State CurrentState { get { return _currentState; } }
public void GoToNextState()
{
if (_currentState.NextState == null)
return;
_prevStates.Push(_currentState);
_currentState = _nextStates.Count > 0 ? _nextStates.Pop() : _currentState.NextState;
}
public void GoToPrevState()
{
// if not the first state
_nextStates.Push(_currentState);
_currentState = _prevStates.Pop();
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With