简体   繁体   中英

How to elegantly create command objects read in from serial port in VB.NET?

In my VB.NET program, I am reading in command responses from a device connected to a serial port. At the moment the command set is fixed, but it is possible that new commands will be added in the future. Is there something more elegant than a huge switch/if-then-else statement I can use to read in each command and create an object for them? I have a base "command" class but derived ones for special functionality each command possesses.

I was hoping to make an elegant solution that avoids updating a giant switch statement each time a new object was added to the command set.

It depends on format of your messages on the wire and how you deserialize them. I would do something like this (example is in C#, as I don't use VB.NET, but it should be easy to convert it).

Each command implements ICommand interface and is derived from some CommandBase implementation class. CommandBase defines the MessageId abstract property which is unique for each command type (you can also use constant). This ID is also part of message header on the wire so that you know what command is comming from the device.

Now you get message ID from the device:

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

You get the correct type from a map which was set up earlier:

Type GetTypeForMessage(int msgId) {
    // m_commandMap is Dictionary<int, Type>
    return m_commandMap[msgId];
}

Now the remaining question is how to setup m_commandMap . One way is to automatically register all classes which derive from some CommandBase class. You do something like this on startup:

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

Now when you need to implement new command, all you have to do is define it:

class NewCommand : CommandBase {
    public override int MessageId { get { return 1234; } }
    // or preferably: public const int MessageId = 1234;
    // the rest of the command: ...
}

It will be automatically registered on startup and used for deserialization if the corresponding ID comes from the device.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM