102

申し訳ありませんが、これに答える質問が見つかりません。他の誰かが以前に提起したことはほぼ確実です。

私の問題は、組み込みデバイスを実行するためにいくつかのシステム ライブラリを作成していることです。無線ブロードキャストを介してこれらのデバイスに送信できるコマンドがあります。これはテキストでのみ実行できます。システムライブラリ内には、次のようなコマンドを処理するスレッドがあります

if (value.equals("A")) { doCommandA() }
else if (value.equals("B")) { doCommandB() } 
else if etc. 

問題は、多くのコマンドがすぐに制御不能になることです。外を見るのは恐ろしく、デバッグするのは苦痛で、数か月で理解するのは気が遠くなるようなものです。

4

15 に答える 15

172

コマンドパターンを使用:

public interface Command {
     void exec();
}

public class CommandA() implements Command {

     void exec() {
          // ... 
     }
}

// etc etc

次に、Map<String,Command>オブジェクトを作成してCommandインスタンスを設定します。

commandMap.put("A", new CommandA());
commandMap.put("B", new CommandB());

次に、 if / else ifチェーンを次のように置き換えることができます。

commandMap.get(value).exec();

編集

UnknownCommandまたはなどの特別なコマンドを追加することもできますが、クライアントのチェックを最小限に抑えるために、これらのコーナー ケースを処理NullCommandする が必要です。CommandMap

于 2009-07-29T11:45:23.597 に答える
12

私の提案は、列挙型とコマンドオブジェクトの一種の軽量な組み合わせです。これは、EffectiveJavaのアイテム30でJoshuaBlochが推奨するイディオムです。

public enum Command{
  A{public void doCommand(){
      // Implementation for A
    }
  },
  B{public void doCommand(){
      // Implementation for B
    }
  },
  C{public void doCommand(){
      // Implementation for C
    }
  };
  public abstract void doCommand();
}

もちろん、doCommandにパラメーターを渡したり、戻り値を指定したりすることもできます。

doCommandの実装が列挙型に実際に「適合」しない場合、このソリューションは実際には適切でない可能性があります。これは、トレードオフを行う必要がある場合は通常、少しあいまいです。

于 2009-07-29T12:09:49.590 に答える
7

dfa によって単純明快に示されているようにインターフェイスを実装することは、クリーンでエレガントです (そして「公式に」サポートされている方法です)。これが、インターフェイスの概念の目的です。

C# では、c で関数ポインターを使用するのが好きなプログラマーのためにデリゲートを使用できますが、DFA の手法を使用する方法です。

あなたも配列を持つことができます

Command[] commands =
{
  new CommandA(), new CommandB(), new CommandC(), ...
}

次に、インデックスでコマンドを実行できます

commands[7].exec();

DFA からの盗作ですが、インターフェースの代わりに抽象基本クラスを持っています。後で使用される cmdKey に注意してください。経験上、機器コマンドにはサブコマンドもあることがよくあります。

abstract public class Command()
{
  abstract public byte exec(String subCmd);
  public String cmdKey;
  public String subCmd;
}

次のようにコマンドを作成します。

public class CommandA
extends Command
{
  public CommandA(String subCmd)
  {
    this.cmdKey = "A";
    this.subCmd = subCmd;
  }

  public byte exec()
  {
    sendWhatever(...);
    byte status = receiveWhatever(...);
    return status;
  }
}

次に、キーと値のペアを吸う関数を提供することで、汎用の HashMap または HashTable を拡張できます。

public class CommandHash<String, Command>
extends HashMap<String, Command>
(
  public CommandHash<String, Command>(Command[] commands)
  {
    this.commandSucker(Command[] commands);
  }
  public commandSucker(Command[] commands)
  {
    for(Command cmd : commands)
    {
      this.put(cmd.cmdKey, cmd);
    }
  }
}

次に、コマンド ストアを作成します。

CommandHash commands =
  new CommandHash(
  {
    new CommandA("asdf"),
    new CommandA("qwerty"),
    new CommandB(null),
    new CommandC("hello dolly"),
    ...
  });

コントロールを客観的に送信できるようになりました

commands.get("A").exec();
commands.get(condition).exec();
于 2009-07-30T02:42:23.080 に答える
7

コマンドの列挙があります。

public enum Commands { A, B, C; }
...

Command command = Commands.valueOf(value);

switch (command) {
    case A: doCommandA(); break;
    case B: doCommandB(); break;
    case C: doCommandC(); break;
}

コマンドがいくつかある場合は、別の場所で回答されているコマンド パターンの使用を検討してください (ただし、HashMap を使用する代わりに、列挙型を保持し、列挙型内の実装クラスへの呼び出しを埋め込むことができます)。例については、この質問に対する Andreas または jens の回答を参照してください。

于 2009-07-29T11:47:26.367 に答える
5

コマンドオブジェクトを作成し、文字列をキーとして使用してハッシュマップに配置することをお勧めします。

于 2009-07-29T11:46:43.280 に答える
3

コマンド パターンのアプローチはベスト プラクティスに近く、長期的には維持しやすいと私は信じていますが、ここに 1 つのライナー オプションがあります。

org.apache.commons.beanutils.MethodUtils.invokeMethod(this,"doCommand"+value,null);

于 2009-07-29T12:00:50.423 に答える
2

私の意見では、@dfa が提供する回答が最良の解決策です。

Java 8を使用していて、Lambda を使用したい場合に備えて、いくつかのスニペットを提供しています。

パラメータなしのコマンド:

Map<String, Command> commands = new HashMap<String, Command>();
commands.put("A", () -> System.out.println("COMMAND A"));
commands.put("B", () -> System.out.println("COMMAND B"));
commands.put("C", () -> System.out.println("COMMAND C"));
commands.get(value).exec();

(Command の代わりに Runnable を使用できますが、意味的に正しいとは考えていません):

パラメーターが 1 つのコマンド:

使用できるパラメーターが必要な場合は、次のようにしますjava.util.function.Consumer

Map<String, Consumer<Object>> commands = new HashMap<String, Consumer<Object>>();
commands.put("A", myObj::doSomethingA);
commands.put("B", myObj::doSomethingB);
commands.put("C", myObj::doSomethingC);
commands.get(value).accept(param);

上記の例では、doSomethingXは のクラスに存在するメソッドであり、myObj任意のオブジェクト (paramこの例では名前が付けられています) を引数として受け取ります。

于 2014-07-02T22:17:25.463 に答える
2

私は通常、そのように解決しようとします:

public enum Command {

A {void exec() {
     doCommandA();
}},

B {void exec() {
    doCommandB();
}};

abstract void exec();
 }

これには多くの利点があります。

1) exec を実装せずに列挙型を追加することはできません。Aを見逃さないように。

2)コマンドマップに追加する必要さえないため、マップを構築するための定型コードはありません。抽象メソッドとその実装だけです。(これは間違いなくボイラープレートでもありますが、これ以上短くなることはありません..)

3) if の長いリストを調べるか、hashCode を計算してルックアップを行うことで、無駄な CPU サイクルを節約できます。

編集: ソースとして列挙型ではなく文字列がある場合は、単にCommand.valueOf(mystr).exec()exec メソッドを呼び出すために使用します。別のパッケージから呼び出す場合は、exec で public 修飾子を使用する必要があることに注意してください。

于 2009-07-29T12:02:16.643 に答える
2

コマンドのマップを使用するのがおそらく最善です。

しかし、これらのセットを処理する必要がある場合は、大量のマップがノックすることになります. 次に、列挙型でそれを行うことを検討する価値があります。

「値」を解決するメソッドを Enum に追加すると、スイッチを使用せずに Enum で実行できます (この例ではおそらくゲッターは必要ありません)。次に、次のことができます。

更新: 各呼び出しでの反復を避けるために静的マップを追加しました。この答えから恥知らずにつままれました。

Commands.getCommand(value).exec();

public interface Command {
    void exec();
}

public enum Commands {
    A("foo", new Command(){public void exec(){
        System.out.println(A.getValue());
    }}),
    B("bar", new Command(){public void exec(){
        System.out.println(B.getValue());
    }}),
    C("barry", new Command(){public void exec(){
        System.out.println(C.getValue());
    }});

    private String value;
    private Command command;
    private static Map<String, Commands> commandsMap;

    static {
        commandsMap = new HashMap<String, Commands>();
        for (Commands c : Commands.values()) {
            commandsMap.put(c.getValue(), c);    
        }
    }

    Commands(String value, Command command) {
        this.value= value;
        this.command = command;
    }

    public String getValue() {
        return value;
    }

    public Command getCommand() {
        return command;
    }

    public static Command getCommand(String value) {
        if(!commandsMap.containsKey(value)) {
            throw new RuntimeException("value not found:" + value);
        }
        return commandsMap.get(value).getCommand();
    }
}
于 2009-07-29T12:26:05.123 に答える
1

複数の複雑な「if」ステートメントがある場合、これはルール エンジンを使用するためのパターンです。たとえば、JBOSS Droolsを参照してください。

于 2009-07-29T12:01:59.937 に答える
0

便利な一連の手順(コマンドと呼ばれるもの)を持つことができれば..

しかし、コードを書くためのプログラムを書くことはできます。すべて非常に体系的です。 if(value='A') commandA(); else if(...................................................など

于 2009-07-29T12:00:29.567 に答える
0

ここで説明されているように、HashMap を使用するだけです。

于 2009-07-29T11:47:06.260 に答える
0

さまざまなコマンドの動作に重複があるかどうかはわかりませんが、複数のコマンドがいくつかの入力値を処理できるようにすることで、柔軟性を高めることができるChain Of Responsibilityパターンを確認することもできます。

于 2009-07-29T13:30:37.787 に答える
-1

それが多くのことを行う場合、多くのコードが存在することになり、そこから逃れることはできません。簡単にフォローできるようにし、変数に非常に意味のある名前を付けてください。コメントも役立ちます...

于 2009-07-29T11:45:52.363 に答える