正确地为表示状态变化的不可变数据结构建模

本文关键字:不可变 数据结构 建模 变化 表示 状态 正确地 | 更新日期: 2023-09-27 18:02:25

我特别使用了immutablemutations来突出这个问题。

我想设计一个抽象的属性来模拟状态转换和收集相关的数据(也许这两个要求违反了Single Responsibility?)

我会这样写:

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是它自己的含义(例如,它包含特定的关联数据)。

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

正确分解状态变化方法:

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

正确地为表示状态变化的不可变数据结构建模

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

如果你想重构它的"面向对象风格",而不关心这是否暴露了你可能的可变实现,那么你可以使用"用子类替换Typecode"重构,最终得到这样的东西:

interface IState
{
    IEnumerable<string> Data { get; }
}
class State1 : IState 
{  ...  }
class State2 : IState 
{  ...  }
class State3 : IState 
{  ...  }

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

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

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

如果没有额外的背景知识,很难确切地说出哪种情况最适合您的应用程序。例如,不清楚这些枚举的目的是什么,因为它们似乎不涉及有关转换的决策。

也许这样的模式会有帮助