2017-12-14 5 views
0

stringからコンソールコマンドを処理するコンソールを作成しようとしています。コンソールコマンドを処理するためのベストJavaデザインパターン

文は、このような各コマンドをチェックするならば私が使用している瞬間

if (command.contains("new train")) { 
    command = command.replace("new train ", ""); 
    Train t = new Train(); 
    t.setCode(command); 
    ServiceProvider.getTrainService().saveOrUpdate(t); 
    responeHandler("train " + command + " created"); 
} 

しかし、これは私の意見で最高のソリューションではありません。

私はこのような問題のための良いデザインパターンが既に存在するのだろうか?

私はbuilderfactoryのパターンを見てきましたが、本当に正しいかどうかは判断できません。

答えて

1

CommandFactoryパターン?

interface Command { 
    void execute(); 
} 

interface CommandFactory { 
    boolean canCreate(String input); 
    Command fromInput(String input); // or return Optional so it can be a FunctionalInterface 
} 

class TrainCommand implements Command { 
    String train; 
    public TrainCommand(String t) { train = t; } 
    public void execute() { 
     ServiceProvider.getTrainService().saveOrUpdate(t); 
    } 
} 

class TrainCommandFactory { 
    public boolean canCreate(String t) { 
     return t.contains("new train "); 
    } 
    public Command fromString(String c) { 
     return new TrainCommand(c.replace("new train ", "")); 
    } 
} 

そして、すべての知られているコマンドの工場を反復処理SingletonComposite CommandFactory:

class CommandFactories implements CommandFactory { 
    private static final CommandFactories INSTANCE; 
    private List<CommandFactory> delegates = Arrays.asList(
     new TrainCommandFactory() 
     // others 
    }; 
    public boolean canCreate(String t) { 
     return delegates.stream() 
      .filter(cf -> cf.canCreate(t)) 
      .findAny().isPresent(); 
    } 
    public Command fromString(String c) { 
     return delegates.stream() 
      .filter(cf -> cf.canCreate(t)) 
      .findAny() 
      .map(CommandFactory::fromString); 
    } 
} 
+0

素晴らしいですが、私のコードは実際に呼び出されるたびにTrainCommandFactoryから新しいコマンドインスタンスを作成する必要がありますか?また、 'Singleton''' Composite'ではどういう意味ですか?コンポジットは、もし私が正しいなら、オブジェクトのツリーのようなものですか?シングルトンは1インスタンスしか保持できません。どのように私はそれら2を接続したいですか? – klokklok

+0

複合シングルトン工場の回答を編集しました。毎回コマンドインスタンスを作成したくない場合は、 'TrainCommand'シングルトンを作成し、' fromString'が呼び出されるたびにファクトリに返すようにすることができます。しかし、String引数を受け入れるには 'execute'を変更する必要があります。 – daniu

+0

ありがとう、私はあなたとMartin Frankの答えを組み合わせて使用​​しました! – klokklok

1

Map<String, Consumer<String>>は、アクションにコマンドを関連付けることができます。
GOFファクトリではなく、DPコマンドです。
しかし、これらは工場とコマンドパターンの公正で簡単な実装です。
それも考慮する必要があります。また、あなたはマップに配置する必要はありません無効なコマンドのための特別なアクションを作成でき

Map<String, Consumer<String>> actionsByCommand = new HashMap<>(); 

actionsByCommand.put("new train", command->{ 
    command = command.replace("new train ", ""); 
     Train t = new Train(); 
     t.setCode(command); 
     ServiceProvider.getTrainService().saveOrUpdate(t); 
     responeHandler("train " + command + " created"); 
}); 

actionsByCommand.put("delete train", command->{ 
    command = command.replace("delete train ", ""); 
    ... 
}); 

// and so for... 

:テスト容易性とアクションクラスの保守性を向上させるために

Consumer<String> invalidCommandAction = (command-> System.out.println("Invalid command. Here are the accepted commands...")); 

を、あなたは 可能性がありそれらを別のクラスに移動します。

public class NewTrainAction implements Consumer<String>{ 

    public void accept(String command){ 
     command = command.replace("new train ", ""); 
     Train t = new Train(); 
     t.setCode(command); 
     ServiceProvider.getTrainService().saveOrUpdate(t); 
      responeHandler("train " + command + " created"); 
    } 

} 

と同じように定義されている他のアクション:として定義NewTrainAction

Map<String, Consumer<String>> actionsByCommand = new HashMap<>(); 

actionsByCommand.put("new train", new NewTrainCommand()); 
actionsByCommand.put("delete train", new DeleteTrainCommand()); 

その後、この方法でそれらを使用することができます。多分

Scanner scanner = new Scanner(System.in); 
while (scanner.hasNextLine()) { 
    String command = scanner.nextLine(); 
    Consumer<String> action = actionsByCommand.getOrDefault(command, invalidCommandAction); 
    action.accept(command); 
} 
+0

この回答で参照できるパターンはありますか?私は、巨大な関数を持つ同じクラスを使う代わりに、私のコマンドをクラスに分割しようとしています。 – klokklok

+0

要件やご質問にお答えするために更新しました。 – davidxxx

0

あなたは春の世界にいる場合、あなたはあなたが

org.springframework.boot.CommandLineRunner 
を実装するために検討することもでき

を使用することができます

各コマンドを実行できました独自のCommandLineRunneインスタンス内に存在します。

使用

org.springframework.core.env.SimpleCommandLinePropertySource 

は、あなたのコマンドライン解析する

1

あなたは配列にあなたの命令を格納することができ、ユーザーがコマンドを入力するとき、あなたは指定されたインデックスを持つ項目を見つけることができます。インデックスは意味があり、スイッチケースで使用可能であり、アイテムが見つからない場合は、意味のある応答を与えることができます。私は定義されたコマンドを使用していると思う

protected String[] supportedCommands = {"first", "second", "third"}; 
public static int findCommand(String command) { 
    for (var i = 0; i < supportedCommands.length; i++) { 
     if (command.equals(supportedCommands[i])) return i; 
    } 
    return -1; //Not found 
} 

public static void handleCommand(String command) { 
    int c = findCommand(command.toLowerCase()); 
    switch (c) { 
     case 1: {/*...*/} break; 
     default: {/*Handle Undefined command*/} 
    } 
} 
+0

私は実際にコマンドを良いパターンのクラスに分割しようとしています。あなたの答えを見ると、私は同じクラスのすべてのコードを書いています。このようにして、私のスイッチのケースとアレイは時間がたつにつれてますます大きくなります。 (新しいコマンドを追加するとき) – klokklok

+0

@klokklok何もエンジンと全く同じクラスにコマンドを保存する必要はありません。好きなように分けることができます。 –

+0

あなたはそうではありません。しかし、私の問題は、これらのクラスを適切な方法で分割する方法がわかりませんでした。私はすべてをお互いの下に置かないようにしたかった。 – klokklok

1

また、あなたは、アレイ内のすべての下の文字を複数のキーを備え、検索の前に下にコマンドキーを回すの大文字と小文字を区別しない方法でこれを行うことができます適切な方法。コマンドの重要な問題は、commandStringで識別され(matches)、実行されることになります(execute)。カスタムCommandを作成したら、リストに登録して実行することができます。あなたはすべてのコマンドリスト(またはその他のコレクション)にこれらのコマンドストアを使用してチェックしたい場合は、あなたのコマンドは入力と一致した場合に

interface Command{ 
    boolean matches(String commandString); 
    boolean execute(String commandString); 
} 

実装例は

CreateTrainCommand implements Command{ 

    private final CMDSTRING = "new train"; 

    @Override 
    public boolean matches(CommandString cmdStr){ 
     if(cmdStr != null && cmdStr.toLowerCase.startsWith(CMDSTRING)){ 
      return true; 
     } 
     return false; 
    } 

    @Override 
    public boolean matches(CommandString cmdStr){ 
     if(cmdStr != null){ 
      String train = cmdString.toLowerCase.replace(CMDSTRING, "").trim(); 

      //here comes your command execution 
      Train t = new Train(); 
      ... 
     } 
     return true;//execution succesful 
    } 
} 

になり

List<Command> cmds = ... 
cmds.add(new CreateTrainCommand()); //add all commands that were implemented 
//or only some commands if the user has restricted access 

ここにコマンドを適用する方法を示します。

String commandString = ... //from Scanner or where else 
for(Command cmd: cmds){ //use streams if you're java 8 
    if (cmd.matches(commandString)){ 
     boolean wasSuccesful = cmd.execute(commandString); 
     break; 
    } 
} 
関連する問題