处理控制台命令的最佳Java设计模式

时间:2017-12-14 11:21:35

标签: java design-patterns console

我正在尝试创建一个控制台来处理来自string的控制台命令。

目前,我使用if语句检查每个命令,如下所示:

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模式,但无法确定它们是否是正确的选择。

5 个答案:

答案 0 :(得分: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..."));

为了提高动作类的可测试性和可维护性,您可以 把它们分成不同的类。

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

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

NewTrainAction定义为:

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");
   }

}

其他动作以同样的方式定义。

然后你可以这样使用它们:

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

答案 1 :(得分: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 ", ""));
    }
}

一个Singleton Composite 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);
    }
}

答案 2 :(得分: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*/}
    }
}

答案 3 :(得分:1)

我认为使用已定义的命令是正确的方法。命令的一个重要问题是由matches识别(commandString)并执行(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(或任何其他集合)中,并检查命令是否与输入匹配

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

答案 4 :(得分:0)

如果你在Spring世界,你可以使用

您可以考虑实施

org.springframework.boot.CommandLineRunner

每个命令都可以在自己的CommandLineRunne实例中执行。

使用

org.springframework.core.env.SimpleCommandLinePropertySource

解析命令行