1

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.

Community
  • 1
  • 1
jay
  • 1,510
  • 2
  • 11
  • 19
  • Is it state machine? If yes there is open source implementations like [stateless] https://code.google.com/p/stateless/ . You can see implementation details there – paramosh Mar 06 '13 at 10:55
  • @paramosh, I would not say it's exactly this (also if I may be wrong). It's the representation of a mutable state. Each state has meaning. Some state mutate a internal data structure. The question is: returning a new instance on each mutation, am I able to define an immutable data structure that models state transitions? Is this the correct shape of such data structure? – jay Mar 06 '13 at 11:14

2 Answers2

1

If you need a guarantee that the State is immutable, you should consider sealing the State class, which would only allow state transitions (into new immutable states) through methods exposed by that one-and-only class, keeping the enums like you are doing so far. Using extension methods is in this case functionally equivalent, as you keep the implementation sealed.

If you want to refactor it "OOP style" and don't care if this exposes you to possible mutable implementations, then you can use the "Replace Typecode with Subclass" refactoring to end up with something like:

interface IState
{
    IEnumerable<string> Data { get; }
}

class State1 : IState 
{  ...  }

class State2 : IState 
{  ...  }

class State3 : IState 
{  ...  }

In this case, you can have extension methods on the IState interface, but it will mean that transition logic will have to be completely separated from the actual state.

Or, you can have the IState interface implement a GetNextState(someInput) method, and then have each state decide which state should be next.

Alternatively, you can have an abstract base class with internal abstract members, to prevent other assemblies from deriving from them.

Without additional background, it's hard to tell exactly which of this cases would suit your application best. For example, it is unclear what the purpose of these enums is, since they don't seem to be involved in the decision about transitions.

vgru
  • 49,838
  • 16
  • 120
  • 201
  • +1 @Groo, you're right about enum. As you see in the second edit I've added an alternative because it smells to something that could lead to __Complex conditionals__. This reply to my question. Very helpful also the indicated refactoring. Regarding the "OOP" style, I was searching a correct pattern from OO perspective with correct functional principles about immutability... – jay Mar 06 '13 at 12:47
-2

Maybe such pattern will be helpful State

Killo
  • 300
  • 1
  • 5
  • also if the State GoF pattern has relations with the question, referencing it doesn't help me much... :/ Look at my reply to the comment of another person (up in the question). I explain better what I exactly ask. – jay Mar 06 '13 at 11:19