如何最好地代表';变体';枚举
本文关键字:变体 枚举 何最好 | 更新日期: 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))
{
//
}
优点
- 您可以轻松添加支持
缺点
- 大量实施
- 当有很多具有不同映射的机器状态版本时,可能会出现一些混乱
- 无法使用开关(必须基于if)