1

我特别玩了不可变突变的词来突出这个问题。

我想设计一个抽象来模拟状态转换并收集相关数据(也许这两个要求违反了单一职责?)。

我会画出类似的东西:

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;
        }
    }
}

并将其用作:

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" }));

上一个问题我知道我可以使用 .NET不可变集合

但这不是我的问题的核心,我想知道这样的设计是否会导致不可变数据结构的正确(或可接受)实现,该结构模型状态与相关数据的转换。

替代实现:

我可以删除枚举并定义 State 的子类型吗?

class State {

  public StateTransition1 ChangeToTransition1() {
    return new StateTransition1( ... );
  }

  public StateTransition2 ChangeToTransition2(IEnumerable data) {
    return new StateTransition2( ... );
  }
}

通过这种方式,我清楚地将transition1 作为定义明确的特定含义,将transition2 作为它自己的含义(例如,它包含特定的关联数据)。

然后消费者代码可以查询子类型类型而不是枚举来控制流程。

正确考虑状态变化方法:

由于使用构造函数重新创建状态,我想知道分解(``ChangeToXXXX`)是否将状态方法更改为扩展方法可以导致更可维护和更正式的正确设计。

4

2 回答 2

1

如果您需要保证sState是不可变的,您应该考虑密封该类State,这将只允许通过该唯一类公开的方法进行状态转换(到新的不可变状态),保持enums 就像您目前所做的一样. 在这种情况下,使用扩展方法在功能上是等效的,因为您保持实现密封。

如果您想将其重构为“OOP 样式”并且不关心这是否会使您接触到可能的可变实现,那么您可以使用“用子类替换类型代码”重构以得到类似的结果:

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

class State1 : IState 
{  ...  }

class State2 : IState 
{  ...  }

class State3 : IState 
{  ...  }

在这种情况下,您可以在IState接口上使用扩展方法,但这意味着转换逻辑必须与实际状态完全分离。

或者,您可以让IState接口实现一个GetNextState(someInput)方法,然后让每个状态决定下一个状态。

或者,您可以拥有一个带有internal abstract成员的抽象基类,以防止其他程序集从它们派生。

如果没有额外的背景,很难准确判断哪种情况最适合您的应用程序。例如,不清楚这些枚举的目的是什么,因为它们似乎不参与有关转换的决定。

于 2013-03-06T12:29:11.363 回答
-2

也许这样的模式会有所帮助

于 2013-03-06T10:52:08.370 回答