在我的 VB.NET 程序中,我正在读取来自连接到串行端口的设备的命令响应。目前命令集是固定的,但将来可能会添加新命令。有没有比我可以用来读取每个命令并为它们创建对象的巨大 switch/if-then-else 语句更优雅的东西?我有一个基本的“命令”类,但派生了每个命令拥有的特殊功能。
我希望做出一个优雅的解决方案,避免每次将新对象添加到命令集中时更新巨大的 switch 语句。
在我的 VB.NET 程序中,我正在读取来自连接到串行端口的设备的命令响应。目前命令集是固定的,但将来可能会添加新命令。有没有比我可以用来读取每个命令并为它们创建对象的巨大 switch/if-then-else 语句更优雅的东西?我有一个基本的“命令”类,但派生了每个命令拥有的特殊功能。
我希望做出一个优雅的解决方案,避免每次将新对象添加到命令集中时更新巨大的 switch 语句。
这取决于线路上消息的格式以及反序列化它们的方式。我会做这样的事情(示例是在 C# 中,因为我不使用 VB.NET,但它应该很容易转换)。
每个命令都实现ICommand
接口,并从某个CommandBase
实现类派生。CommandBase
定义MessageId
每个命令类型唯一的抽象属性(您也可以使用常量)。此 ID 也是线路上消息头的一部分,以便您知道来自设备的命令。
现在您从设备获取消息 ID:
int msgId = ... // what came from the device
Type cmdType = GetTypeForMessage(msgId); // get the corresponding implementation
ICommand cmd = (Command)Activator.CreateInstance(cmdType); // crate an instance
cmd.Deserialize(buffer); // or whatever way you do the serialization
cmd.Execute(); // run the command
您可以从之前设置的地图中获得正确的类型:
Type GetTypeForMessage(int msgId) {
// m_commandMap is Dictionary<int, Type>
return m_commandMap[msgId];
}
现在剩下的问题是如何设置m_commandMap
。一种方法是自动注册从某个类派生的所有CommandBase
类。你在启动时做这样的事情:
// find all types in this assembly
Assembly assembly = Assembly.GetExecutingAssembly();
foreach (var type in assembly.GetTypes()) {
if(typeof(CommandBase).IsAssignableFrom(type)) { // which derive from CommandBase
CommandBase cmd = (CommandBase) Activator.CreateInstance(type);
m_commandMap[cmd.MessageId] = type;
// I would make MessageId a static constant on class and read it
// using reflection, so I don't have to instantiate an object
}
}
现在,当您需要实现新命令时,您所要做的就是定义它:
class NewCommand : CommandBase {
public override int MessageId { get { return 1234; } }
// or preferably: public const int MessageId = 1234;
// the rest of the command: ...
}
如果对应的ID来自设备,它将在启动时自动注册并用于反序列化。