如何最好地代表'变体'枚举?

时间:2014-10-22 09:15:53

标签: c#

不幸的是,我不得不与一些看似处于不断变化的固件接口(USB通信协议不断变化,寄存器/地址映射不断变化,各种状态机不断变化等)。< / p>

我设法实现了一个合理的界面,所以实际上与固件通信并不太困难。

我的主要问题是代表不同的状态机,其中大约有十几个。在他们改变之前我只有一个枚举,例如:

public enum Component1StateType
{
    RESET        = 0,
    INITIALISING = 1,
    READY        = 2,
    WAITING      = 3
}

然后我在主代码中自由使用,根据系统状态修改软件的行为。

现在我遇到了一个问题,即较新的固件Component1StateType已更改为以下内容:

public enum Component1StateType
{
    RESET        = 0,
    INITIALISING = 1,
    INITIALISED  = 2,
    READY        = 3,
    ERROR        = 4,
    STANDBY      = 5,
    WAITING      = 6
}

然后会破坏主程序中所有以前的状态处理代码,当然必须支持所有不同版本的固件。

我正在努力想出一种很好的方式来代表这些不同的状态机,这意味着主代码不会像以下那样散落:

if (stateMachineVersion == 1)
{
    //code branch for version 1
}
else if(stateMachineVersion == 2)
{
    //code branch for version 2
}
...
...

关于如何最好地应对这些不断变化的状态的任何想法?

3 个答案:

答案 0 :(得分:2)

为状态机创建一个接口,并在顶层公开enum,而不向状态机类外的程序提供任何数值:

interface HardwareConnector {
    Component1StateType CurrentState {get;}
    ... // Other properties and methods
}

public enum Component1StateType {
    Reset
,   Initializing
,   Initialized
,   Ready
,   Error
,   Standby
,   Waiting
}

根据需要支持的硬件版本,对HardwareConnector接口进行不同的实现。您的代码应该编程到HardwareConnector接口。代码唯一应该知道的地方#34;关于每个硬件版本的类实现是初始化,在那里你检测另一端的硬件。

在每个实现类中,您可以将私有enum与接口级别可见的Component1StateType枚举断开连接。

internal enum Component1StateTypeV1 {
    RESET        = 0,
    INITIALISING = 1,
    READY        = 2,
    WAITING      = 3
}

internal enum Component1StateTypeV2 {
    RESET        = 0,
    INITIALISING = 1,
    INITIALISED  = 2,
    READY        = 3,
    ERROR        = 4,
    STANDBY      = 5,
    WAITING      = 6
}

该类的内部使用私有enum值来执行其工作,然后在State getter的实现中将其私有值转换为公共值:

public Component1StateTypeV {
    get {
        switch (internalState) {
            case Component1StateTypeV1.RESET : return Component1StateTypeV.Reset;
            case Component1StateTypeV1.INITIALISING : return Component1StateTypeV.Initializing;
            case Component1StateTypeV1.READY : return Component1StateTypeV.Ready;
            case Component1StateTypeV1.WAITING : return Component1StateTypeV.Waiting;
        }
        throw new InvalidOperationException();
    }
}

答案 1 :(得分:1)

我有一些真实世界的经验,因为我维持一项服务,必须处理来自各种远程信息处理跟踪单元(&#34;调制解调器&#34;)的数千个并发连接,其中一些是主题的变体来自同一制造商,其中一些有非常不同的协议。当我第一次遇到这个代码时,大部分工作都是在一个单独的类中完成的,如果我没记错的话,大约有10,000行代码!几年前我将它重构为一个OOP模型,从那时起它就变得更加容易。

我有一个用于调制解调器的抽象基类,以及每种调制解调器类型的不同级别的派生类。在游戏中还有几个代表各种行为的界面。我建议你重构如下内容:

internal abstract class Device // Generic name here as I don't know what kind of device you are talking to {
    // Common code here
    // abstract and/or virtual members here for the different behaviours in the various firmware versions        
}

internal sealed class DeviceFirmwareA : Device
{
     // Private, firmware-specific enumerations here
     // Overrides here
}

internal sealed class DeviceFirmwareB : Device
{
     // Private, firmware-specific enumerations here
     // Overrides here
}

当然你还需要:

  • 固件检测和实例化适当的&#34;解码器&#34; 类
  • 如果需要公开Component1StateType,您可能需要进行一些映射或进行其他更改。

对不起,我对其他步骤含糊不清,因为我需要查看相关的现有代码,但如果沿着这条路走下去,我确定你可以使用它出!

答案 2 :(得分:0)

嗯,我的建议: 在旧版本的库存值之后添加缺失状态:

public enum Component1StateType
{
    RESET        = 0,
    INITIALISING = 1,
    READY        = 2,
    WAITING      = 4,
    INITIALISED  = 5,
    // etc
}

它不会破坏旧版本。

接下来,准备可以这样使用的属性:

public enum Component1StateType
{
    RESET        = 0,
    INITIALISING = 1,
    [StateMapping(MachineStateVersion = 2, Value = 3)]
    READY        = 2,
    [StateMapping(MachineStateVersion = 2, Value = 4)]
    WAITING      = 3,
    [StateMapping(MachineStateVersion = 2, Value = 2)]
    INITIALISED  = 5,
    // etc
}

该属性可以多次用于各种范围的machnie state版本。 然后准备扩展方法:

public static bool IsInState(this Enum enum, int machineStateVersion, Component1StateType value)
{
    // fetch proper mapped value via reflection from attribute basing on machineState
}

用法:

Component1StateType currentState = // val from some;  
if (currentState.IsInState(CURRENT_MACHINE_STATE_VERSION, 
                                Component1StateType.INITIALISING)) 
{
  //
}

<强>赞成

  1. 您可以轻松添加支持
  2. 的更多版本

    <强>缺点

    1. 许多实施
    2. 当存在大量具有不同映射的机器状态版本时,可能会发生一些混乱。
    3. 不能使用开关(必须以ifs为基础)