我特意使用 immutable 和 mutation 这些词来突出显示问题。
我想对属性设计进行抽象,模拟状态转换并收集相关数据(可能这两个要求违反单一责任?)。
我画了类似的东西:
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" }));
从previous question我知道我可以使用.NET immutable collections。
但这不是我的问题的中心,我想知道这样的设计是否导致了一个不可变数据结构的正确(或可接受)实现,该数据结构模拟状态转换与相关数据。
替代实施:
我可以删除枚举并定义State的子类型吗?
class State {
public StateTransition1 ChangeToTransition1() {
return new StateTransition1( ... );
}
public StateTransition2 ChangeToTransition2(IEnumerable data) {
return new StateTransition2( ... );
}
}
通过这种方式,我明确指出transition1作为一个明确定义的特定含义,transition2作为它自己的含义(例如它包含特定的相关数据)。
然后,消费者代码可以查询子类型而不是枚举来控制流。
正确分解状态变更方法:
由于状态是使用构造函数重新创建的,我想知道是否因子分析(``ChangeToXXXX`)会改变状态方法,因为扩展方法可以导致更可维护和正式的设计。
答案 0 :(得分:1)
如果你需要保证State
是不可变的,你应该考虑密封 State
类,它只允许状态转换(进入新的不可变状态)这个独一无二的类暴露的方法,保持enum
就像你到目前为止一样。在这种情况下,使用扩展方法在功能上是等效的,因为您保持实现密封。
如果你想重构它的“OOP风格”并且不关心这是否会让你暴露于可能的可变实现,那么你可以使用"Replace Typecode with Subclass"重构来结束类似的事情:
interface IState
{
IEnumerable<string> Data { get; }
}
class State1 : IState
{ ... }
class State2 : IState
{ ... }
class State3 : IState
{ ... }
在这种情况下,您可以在IState
接口上使用扩展方法,但这意味着转换逻辑必须与实际状态完全分离。
或者,您可以让IState
接口实现GetNextState(someInput)
方法,然后让每个州决定下一个状态。
或者,您可以拥有一个带有internal abstract
成员的抽象基类,以防止其他程序集从中派生。
如果没有其他背景知识,很难确切地说明哪种情况最适合您的应用。例如,目前尚不清楚这些枚举的目的是什么,因为它们似乎没有参与有关转换的决定。
答案 1 :(得分:-2)
也许这样的模式会有所帮助State