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

本文关键字:变体 枚举 何最好 | 更新日期: 2023-09-27 18:27:21

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

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

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

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

关于如何最好地应对这些不断变化的状态,有什么想法吗?

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

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

interface HardwareConnector {
    Component1StateType CurrentState {get;}
    ... // Other properties and methods
}
public enum Component1StateType {
    Reset
,   Initializing
,   Initialized
,   Ready
,   Error
,   Standby
,   Waiting
}

根据需要支持的硬件版本,对HardwareConnector接口进行不同的实现。您的代码应该是针对HardwareConnector接口进行编程的。代码应该"了解"每个硬件版本的类实现的唯一地方是初始化,在那里您可以检测到另一端的硬件。

在每个实现类中,可以有一个从接口级别可见的Component1StateType枚举断开连接的专用enum

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

我在维护一项服务时有一些实际经验,该服务必须处理来自各种远程信息处理跟踪单元("调制解调器")的数千个并发连接,其中一些是同一制造商主题的变体,另一些则有着截然不同的协议。当我第一次遇到这个代码时,大部分工作都是在一个类中完成的,如果我没有记错的话,这个类大约有10000行代码!几年前,我将其重构为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
}

当然,您还需要:

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

很抱歉,我对其他步骤含糊其辞,因为我需要查看有问题的现有代码,但如果你走这条路,我相信你能解决!

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

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状态版本。然后准备扩展方法:

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. 您可以轻松添加支持

缺点

  1. 大量实施
  2. 当有很多具有不同映射的机器状态版本时,可能会出现一些混乱
  3. 无法使用开关(必须基于if)