[英]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");
}
但这并不是我认为的最佳解决方案。
我想知道是否已经有一个好的设计模式来解决这样的问题?
我已经研究了builder
和factory
模式,但无法真正确定它们是否是正确的选择。
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);
}
可能是Command
和Factory
模式?
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.