创建控制台命令列表-如何正确使用Java

创建控制台命令列表-如何正确使用Java,java,enums,switch-statement,ordinal,Java,Enums,Switch Statement,Ordinal,对于我用Java编写的作业(我是新手),我遇到了创建控制台命令列表的问题。用户将面临一组命令,其中他/她将用数字选择他/她的选择。大概是这样的: Enter your choice: 0) Create a Table 1) List All Tables 2) Delete a Table 3) Insert a Record 4) List All Records 5) Delete a Record 6) Find a Record(by =) 7) Find a Record(by &g

对于我用Java编写的作业(我是新手),我遇到了创建控制台命令列表的问题。用户将面临一组命令,其中他/她将用数字选择他/她的选择。大概是这样的:

Enter your choice:
0) Create a Table
1) List All Tables
2) Delete a Table
3) Insert a Record
4) List All Records
5) Delete a Record
6) Find a Record(by =)
7) Find a Record(by >)
8) Find a Record(by <)
9) Exit
输入您的选择:
0)创建一个表
1) 列出所有表格
2) 删除表格
3) 插入记录
4) 列出所有记录
5) 删除记录
6) 查找记录(按=)
7) 查找记录(通过>)

8) 查找记录(通过是否使用是作弊?;-)

使用序数作为标识符被认为是相当糟糕的形式,但也许您可以这样使用它

private enum Command {
    CREATE_TABLE("Create a Table"),
    ...
    EXIT("Exit");

    private static final Map<Integer, Command> fromOrdinal;

    static {
        fromOrdinal = new HashMap<Integer, Command>();
        for (Command c : values()) {
            fromOrdinal.put(c.ordinal(), c);
        }
    }

    public static Command fromId(int commandId) {
        return fromOrdinal.get(c);
    }

    private final String message;

    Command(String message) {
        this.message = message;
    }

    public String message() { return this.message; }
}
私有枚举命令{
创建表格(“创建表格”),
...
退出(“退出”);
私有静态最终映射fromOrdinal;
静止的{
fromOrdinal=newHashMap();
for(命令c:values()){
fromOrdinal.put(c.ordinal(),c);
}
}
公共静态命令fromId(int commandId){
从序号get返回(c);
}
私有最终字符串消息;
命令(字符串消息){
this.message=消息;
}
公共字符串消息(){返回this.message;}
}
在命令处理类中:

    ...
    Map<Command, Runnable> actions = new HashMap<Command, Runnable>(); // Fill this map with commands and implementations of runnable that does what the command should.

    ...
    void run(int command) {
        Runnable action = actions.get(Command.fromId(command));
        if (action == null)
            throw new IllegalArgumentException("No such command");
        action.run();
    }
。。。
映射操作=新建HashMap();//用命令和runnable的实现填充此映射,以实现命令应该执行的功能。
...
无效运行(int命令){
Runnable action=actions.get(Command.fromId(Command));
if(action==null)
抛出新的IllegalArgumentException(“无此类命令”);
action.run();
}
但是,如果您不想使用enum路径,那么没有什么可以真正阻止您创建非enum命令对象。这样做的好处是,它们可以实现,或者可以配置为调用实际命令的实现:

interface Command {
    char getInputChar();
    String getMessage();
    void run();
}

Next step is to create a Map<Character, Command> constructed from map.put(c.getInputChar(), c) for all commands you want to use, much like the enum example. To execute you can just execute the run() method of the command.
接口命令{
char getInputChar();
字符串getMessage();
无效运行();
}
下一步是为所有要使用的命令创建一个由Map.put(c.getInputChar(),c)构造的映射,非常类似于enum示例。要执行,只需执行命令的run()方法。

好吧,这是一种奇怪的方法,但是如果您只想了解枚举的使用,那么不建议使用“ordinal()

最好创建一个包含2个成员的枚举。不太理想,但可能会有帮助

private enum Command {
        CREATE_TABLE(0,"Create a Table"),
        LIST_ALL_TABLES(1,"List All Tables"),
        DELETE_TABLE(2,"Delete a Table"),
        INSERT_RECORD(3,"Insert a Record"),
        LIST_ALL_RECORDS(4,"List All Records"),
        DELETE_RECORD(5,"Delete a Record"),
        FIND_RECORD_EQ(6,"Find a Record(by =)"),
        FIND_RECORD_GT(7,"Find a Record(by >)"),
        FIND_RECORD_LT(8,"Find a Record(by <)"),
        EXIT(9,"Exit");

        private final String message;
        private final int code;

        public static Command get(int code) {
            for(Command c : Command.values()) {
                if(code==c.code) {
                    return c;
                }
            }
            return null;
        }

        Command(int code, String message) {
            this.code= code;
            this.message = message;
        }
        public int getCode() { return this.code; }
        public String message() { return this.message; }
    }

您可以使用
命令.values()
,它按顺序包含枚举:

switch (Command.values[number]) {
case CREATE_A_TABLE:
...
}
一种更优雅、更易于维护的方法是使用多态性消除switch语句:

abstract class Command {
    private String name;

    protected Command(String name) {
        this.name = name;
    }

    @Override public String toString() {
        return name;
    }

    public abstract void execute();
}
以及其他地方:

Command[] commands = {
    new Command("Create a table") {
        @Override public void execute() {
            // code to create a table
        }
    },
    new Command("List all tables") {
        @Override public void execute() {
            // code to list all tables
        }
    }
};

for (int i = 0; i < commands.length; i++) {
    System.out.println(i + ":" + command);
}

int number = getInput();

commands[number].execute();
Command[]命令={
新建命令(“创建表”){
@重写公共void execute(){
//创建表的代码
}
},
新命令(“列出所有表”){
@重写公共void execute(){
//列出所有表的代码
}
}
};
for(int i=0;i
优点:

  • 更短、更清晰的代码
  • 编译器将检查每个命令是否已实现(使用switch语句,您可能会忘记添加case语句,这是一个仅在运行时才会发生的错误。诚然,如果您在切换枚举时忘记了case,那么好的编译器将发出警告,但与编译错误相比,错过警告的可能性更大)。==>在维护过程中更加健壮

嗯,我要问的部分与家庭作业的本质完全无关,我只是想学习;因此,从任何意义上讲,这都不是作弊。但是,我希望在不使用任何其他库的情况下完成此操作。只是一个noob问题:“static{fromOrdinal=…}”部分是什么?它是一个静态初始值设定项块,您可以使用它来计算静态值等。它在类加载后的一段时间内,在首次访问该类之前运行一次。但不要做任何可能失败的事情,因为静态代码中的异常会使内容以奇怪的方式中断,有时甚至难以调试。这似乎比必要的要复杂得多:既然每个枚举类已经提供了按声明顺序返回枚举的values()方法,为什么还要手动创建fromOrdinal映射呢?嗨,Henrik。非常相似,但两者都有缺点。我现在意识到我应该推荐一个枚举集来代替。。。这种方法的问题是(除了它的优点之外)如果我想添加一个ID为1的新命令,那么修改整个列表对我来说是不好的;我需要改变所有其他的价值观。。我不想手动修改ID。。也许摆脱第一个成员并简单地使用序号会有所帮助?你是对的,这是一个问题,@meriton的答案要好得多!
switch (Command.values[number]) {
case CREATE_A_TABLE:
...
}
abstract class Command {
    private String name;

    protected Command(String name) {
        this.name = name;
    }

    @Override public String toString() {
        return name;
    }

    public abstract void execute();
}
Command[] commands = {
    new Command("Create a table") {
        @Override public void execute() {
            // code to create a table
        }
    },
    new Command("List all tables") {
        @Override public void execute() {
            // code to list all tables
        }
    }
};

for (int i = 0; i < commands.length; i++) {
    System.out.println(i + ":" + command);
}

int number = getInput();

commands[number].execute();