I specially played with words immutable and mutations to highlight the question.
I want to property design an abstraction that model a state transitions and collects related data (maybe that this two requirements violates Single Responsibility?).
I'd sketch something like:
enum StateTypeEnum
{
State1,
State2,
State3
}
class State
{
private readonly StateTypeEnum _stateType;
private readonly IEnumerable<string> _stateData;
public State(StateTypeEnum stateType)
{
_stateType = stateType;
_stateData = new List<string>(new string[] { });
}
public State(StateTypeEnum stateType, IEnumerable<string> stateData)
{
_stateType = stateType;
_stateData = new List<string>(stateData);
}
public State ChangeState(StateTypeEnum stateType)
{
return new State(stateType, _stateData);
}
public State ChangeState(StateTypeEnum stateType, IEnumerable<string> stateData)
{
return new State(stateType, _stateData.Concat(stateData));
}
public State ChangeState(IEnumerable<string> stateData)
{
return new State(_stateType, _stateData.Concat(stateData));
}
public IReadOnlyList<string> StateData
{
get
{
return new ReadOnlyCollection<string>(_stateData.ToList());
}
}
public StateTypeEnum CurrentStateType
{
get
{
return _stateType;
}
}
}
and use it as:
var state1 = new State(StateTypeEnum.State1);
var state2 = state1.ChangeState(StateTypeEnum.State3);
var state3 = state2.ChangeState(new[] { "a", "b", "c" });
var state4 = state3.ChangeState(StateTypeEnum.State1, new[] { "d", "e" });
var state5 = state4.ChangeState(StateTypeEnum.State3);
Debug.Assert(
state5.CurrentStateType == StateTypeEnum.State3);
Debug.Assert(
state5.StateData.SequenceEqual(new[] { "a", "b", "c", "d", "e" }));
From a previous question I know that I can use .NET immutable collections.
But this not central to my question here, I want know if such design lead to a correct (or acceptable) implementation of an immutable data structure that model states transitions with related data.
Alternative implementation:
Can I remove the enumeration and define a sub-type of State?
class State {
public StateTransition1 ChangeToTransition1() {
return new StateTransition1( ... );
}
public StateTransition2 ChangeToTransition2(IEnumerable data) {
return new StateTransition2( ... );
}
}
In this way I clearly that transition1 as a well defined particular meaning and transition2 as its own (e.g. it contains specific associated data).
Then the consumer code can query sub-types type instead of enum to control flow.
Properly factoring state change methods:
Since states are re-created using constructor, I'm wondering if factoring (``ChangeToXXXX`) change state methods as extension methods could lead to more maintainable and formal correctly design.