Nel mio attuale corso di ingegneria del software, il mio team sta lavorando su un sistema di gestione della libreria che è essenzialmente un ambiente di riga di comando / REPL con una mezza dozzina di comandi, ad es. checkout
, search
, ecc. Abbiamo scelto di usare lo schema di comando per questo, quindi ogni comando sarà rappresentato come una classe. Ora, c'è la domanda su come creiamo nuovi oggetti comando quando l'utente inserisce una stringa.
-
Usa condizioni:
switch(input) { case "checkout": return new CheckoutCommand(args); break; // etc.
-
Crea una stringa di mappatura HashMap ai costruttori:
interface CommandCreator { abstract public Command createCommand(String[] args); }
Map<String, CommandCreator> commandMap = new HashMap<>(); commandMap.put("checkout", CheckoutCommand::new); commandMap.put("search", SearchCommand::new); // etc.
return commandMap.get(input).createCommand(args);
-
Usa riflessione:
Class.forName("library.commands." + input).getConstructor(args.getClass()).newInstance(args);
Le classi di comando avranno quindi gli stessi nomi delle stringhe che le invocano, cioè
class checkout implements Command { // class body }
-
Qualcos'altro.
Abbiamo convenuto che il # 1 non è la soluzione migliore. Alcuni di noi pensano che il # 2 non dovrebbe essere usato, perché viola il Principio Aperto / Chiuso; quando aggiungiamo più comandi in futuro, dovremmo modificare la classe che popola il comando HashMap. Alcuni di uso pensano che il # 3 non dovrebbe essere usato, perché la riflessione dovrebbe essere usata solo come ultima risorsa, e sembra troppo "hacky". Tuttavia, nessuno di noi ha idee migliori.
Dovremmo usare # 2 o # 3, o c'è una soluzione migliore?