1

私の VB.NET プログラムでは、シリアル ポートに接続されたデバイスからのコマンド応答を読み込んでいます。現時点ではコマンド セットは固定されていますが、将来的に新しいコマンドが追加される可能性があります。各コマンドを読み取り、それらのオブジェクトを作成するために使用できる巨大な switch/if-then-else ステートメントよりもエレガントなものはありますか? 私は基本「コマンド」クラスを持っていますが、各コマンドが持つ特別な機能のための派生クラスです。

新しいオブジェクトがコマンド セットに追加されるたびに巨大な switch ステートメントを更新することを回避する、洗練されたソリューションを作成したいと考えていました。

4

1 に答える 1

4

これは、ネットワーク上のメッセージの形式と、それらを逆シリアル化する方法によって異なります。私はこのようなことをします(VB.NETを使用していないため、例はC#にありますが、変換は簡単なはずです)。

各コマンドはインターフェイスを実装し、いくつかの実装クラスICommandから派生します。各コマンド タイプに固有の抽象プロパティを定義します (定数も使用できます)。この ID はネットワーク上のメッセージ ヘッダーの一部でもあるため、デバイスから送信されたコマンドを知ることができます。CommandBaseCommandBaseMessageId

デバイスからメッセージ 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です。1 つの方法は、あるクラスから派生するすべてのクラスを自動的に登録すること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 がデバイスから取得された場合、起動時に自動的に登録され、逆シリアル化に使用されます。

于 2013-03-12T10:00:39.993 に答える