繁体   English   中英

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

[英]Best Java design pattern to handle console commands

我正在尝试创建一个控制台来处理来自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模式,但无法真正确定它们是否是正确的选择。

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

可能是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,它可以迭代所有已知的Command Factories:

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

您可以将命令存储在数组中,并且当用户输入命令时,可以找到具有给定索引的项目。 该索引在切换情况下将有意义且可用,如果找不到该项目,则可以给出有意义的响应。

另外,您可以采用不区分大小写的方式执行此操作,即使数组中的键全部为小写字母,然后在搜索之前将命令键降低为小写:

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

我认为使用定义的命令是正确的方法。 命令的一个重要问题将是由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;
    }
}

如果您在春季世界,可以使用

您可以考虑实施

org.springframework.boot.CommandLineRunner

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

采用

org.springframework.core.env.SimpleCommandLinePropertySource

解析您的命令行

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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