Domanda

Per un compiti a casa in Java (con cui sono un principiante), mi imbatto nel problema della creazione di un elenco di comandi console. L'utente si confronterà con un set di comandi, tra cui sceglierà la sua scelta con un numero. Qualcosa come questo:

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

Il primo modo in cui l'ho fatto è il seguente (parti di codice non necessarie sono troncate):

...

outerLoop: while (true) {
    Scanner s = new Scanner(System.in);
    try {
        while (true) {
            System.out.println("Enter your choice:");
            displayChoiceList();
            int choice = s.nextInt();
            switch (choice) {
            case 1:
                processTableCreation();
                break;
            case 2:
                catalog.listAllTables();
                break;
            case 3:
                System.out.println("Enter the name of table:");
                String tableName = s.nextLine();
                catalog.deleteTable(tableName);
                break;
            case 4:
                processRecordInsertion();
                break;
            case 5:
                processListAllRecords();
                break;
            case 6:
                processDeleteRecord();
                break;
            case 7:
                processFindRecord(Constants.Operator.EQUAL);
                break;
            case 8:
                processFindRecord(Constants.Operator.SMALLER);
                break;
            case 9:
                processFindRecord(Constants.Operator.GREATER);
                break;
            case 10:
                break outerLoop;
            }
        }
    } catch (IllegalArgumentException e) {
        System.out.println("Error: " + e.getMessage());
    } catch (IOException e) {
        System.out.println(e.getMessage());
        return;
    }
}

...

private static void displayChoiceList() {
    String[] choices = new String[] { "Create Table", "List All Tables",
            "Delete a Table", "Insert a Record to a Table",
            "List all records", "Delete a record",
            "Find by Primary Key(=)", "Find by Primary Key(<)",
            "Find by Primary Key(>)", "Exit" };
    int id = 0;
    for (String choice : choices) {
        System.out.println((id + 1) + ") " + choice);
        ++id;
    }
}

Quindi, pensando che questo sia brutto, e per il gusto di sperimentare con Enums, ho provato quanto segue:

private enum Command {
    CREATE_TABLE("Create a Table"),
    LIST_ALL_TABLES("List All Tables"),
    DELETE_TABLE("Delete a Table"),
    INSERT_RECORD("Insert a Record"),
    LIST_ALL_RECORDS("List All Records"),
    DELETE_RECORD("Delete a Record"),
    FIND_RECORD_EQ("Find a Record(by =)"),
    FIND_RECORD_GT("Find a Record(by >)"),
    FIND_RECORD_LT("Find a Record(by <)"),
    EXIT("Exit");

    private final String message;

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

    public String message() { return this.message; }
}

...

outerLoop: while (true) {
    Scanner s = new Scanner(System.in);
    try {
        while (true) {
            System.out.println("Enter your choice:");
            displayChoiceList();
            int choice = s.nextInt();

            if (choice == Command.CREATE_TABLE.ordinal())
                processTableCreation();
            else if (choice == Command.LIST_ALL_TABLES.ordinal())
                catalog.listAllTables();
            else if (choice == Command.DELETE_TABLE.ordinal()) {
                System.out.println("Enter the name of table:");
                String tableName = s.nextLine();
                catalog.deleteTable(tableName);                     
            }
            else if (choice == Command.INSERT_RECORD.ordinal())
                processRecordInsertion();
            else if (choice == Command.LIST_ALL_RECORDS.ordinal())
                processListAllRecords();
            else if (choice == Command.DELETE_RECORD.ordinal())
                processDeleteRecord();
            else if (choice == Command.FIND_RECORD_EQ.ordinal())
                processFindRecord(Constants.Operator.EQUAL);
            else if (choice == Command.FIND_RECORD_LT.ordinal())
                processFindRecord(Constants.Operator.SMALLER);
            else if (choice == Command.FIND_RECORD_GT.ordinal())
                processFindRecord(Constants.Operator.GREATER);
            else if (choice == Command.EXIT.ordinal())
                break outerLoop;
            else
                System.out.println("Invalid command number entered!");
        } 
    } catch (IllegalArgumentException e) {
        System.out.println("Error: " + e.getMessage());
    } catch (IOException e) {
        System.out.println(e.getMessage());
        return;
    }
}

...

private static void displayChoiceList() {
    for (Command c : Command.values())
        System.out.println(c.ordinal() + ") " + c.message());
}

In realtà, quello che ho in mente è usare Enum in Switch con i loro valori ordinali, ma Java non consente valori non costante nei casi di switch. Qual è il modo giusto per risolvere questo problema; Il più elegante/scalabile/flessibile? Eventuali commenti costruttivi sono molto apprezzati!

È stato utile?

Soluzione

Potresti usare Command.values(), che contiene gli enum nell'ordine ordinale:

switch (Command.values[number]) {
case CREATE_A_TABLE:
...
}

Un modo più elegante e gestibile è quello di eliminare l'affermazione dell'interruttore usando il polimorfismo:

abstract class Command {
    private String name;

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

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

    public abstract void execute();
}

e altrove:

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

Vantaggi:

  • Codice più corto e più chiaro
  • Il compilatore verifica che ogni comando sia implementato (con un'istruzione switch, è possibile dimenticare di aggiungere un'istruzione Case, un errore che si verificherà in fase di esecuzione solo. Concesso, un buon compilatore emetterà un avviso se si dimentica un caso quando si cambia ENUMS , ma è più probabile che un avvertimento venga perso rispetto a un errore di compilazione). ==> Più robusto durante la manutenzione.

Altri suggerimenti

Userebbe Jakarta Commons Cli essere barare? ;-)

Bene, è un approccio curioso, ma se vuoi solo guardare l'uso di enum, allora "ordinale ()" non è consigliato.

Meglio fare un enum con 2 membri. Non è l'ideale, ma potrebbe aiutare ..

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

Ora puoi ottenere un enum usando lo statico Command.get(int)

    private static void runCommand(int choice) {
        Command command = Command.get(choice);
        System.out.println("You Chose '"+command.message()+"'\n\n");
        switch(command) {
                ....
        }
    }

L'uso degli ordinali come identificatori è considerato una forma piuttosto scarsa, ma forse potresti usarlo in questo modo?

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

E nella tua classe di gestione dei comandi:

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

Ma se non vuoi seguire il percorso Enum non c'è davvero nulla che ti impedisca di creare oggetti di comando non inumi. Il lato positivo è che possono implementare o possono essere configurati per chiamare l'implementazione, il comando effettivo:

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.
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top