# Befehle Command-Pattern mit Registry — passt zum Aufgaben-Tipp „switch, if-else, HashMap" und macht die HashMap-Variante explizit. ## Interface ```java public interface Command { /** Wird vom Parser nach Tokenisierung aufgerufen. */ void execute(GameContext ctx, List args); /** Short help text for the 'help' command. */ String help(); } ``` ## Registry ```java public class CommandRegistry { private final Map commands = new HashMap<>(); public void register(Command cmd, String... names) { for (String n : names) { commands.put(n.toLowerCase(), cmd); } } public Optional find(String verb) { return Optional.ofNullable(commands.get(verb.toLowerCase())); } } ``` **Aliase per Mehrfach-Registrierung:** ```java registry.register(new GoCommand(), "go", "move"); registry.register(new TakeCommand(), "take", "pick"); ``` ## Parser Einfacher tokenisierender Parser. Erst-Token = Verb, Rest = Argumente. Spezialfall: Präpositionen / Artikel wegfiltern, damit `go to north` und `go north` beide funktionieren. ```java public record ParsedCommand(String verb, List args) {} public class CommandParser { private static final Set FILLERS = Set.of("to", "with", "at", "the", "a", "an"); public ParsedCommand parse(String input) { String[] tokens = input.trim().toLowerCase().split("\\s+"); if (tokens.length == 0 || tokens[0].isEmpty()) { return new ParsedCommand("", List.of()); } String verb = tokens[0]; List args = Arrays.stream(tokens, 1, tokens.length) .filter(t -> !FILLERS.contains(t)) .toList(); return new ParsedCommand(verb, args); } } ``` ## Befehlsliste (Pflicht + Optional) | Verb | Aliase | Wirkung | Quelle | |---|---|---|---| | `go ` | `move`, `walk` | Spieler wechselt Raum | Pflicht | | `take ` | `pick`, `get` | Item aus Raum ins Inventar | Pflicht | | `drop ` | `put` | Item aus Inventar in Raum | sinnvoll | | `use ` | — | Item-spezifische Aktion | Pflicht | | `read ` | — | Spezialfall von use für `readable` | Pflicht | | `inventory` | `inv`, `i` | Inventar anzeigen | sinnvoll | | `look` | `l` | Raumbeschreibung wiederholen | sinnvoll | | `examine ` | `x` | Item-Beschreibung anzeigen | sinnvoll | | `talk ` | `speak` | NPC-Greeting ausgeben | NPC-Bonus | | `give ` | — | Item übergeben, Reaktion auslösen | NPC-Bonus | | `help` | `?` | Befehlsübersicht | sinnvoll | | `quit` | `exit` | Spiel beenden | sinnvoll | ## Behandlung unbekannter Befehle ```java parser.parse(input); registry.find(parsed.verb()) .ifPresentOrElse( cmd -> cmd.execute(ctx, parsed.args()), () -> ctx.io().write("I don't understand '" + parsed.verb() + "'. Type 'help'.") ); ``` ## GameContext Wird allen Commands gereicht, kapselt was sie ändern dürfen: ```java public class GameContext { private final World world; private final Player player; private final GameIO io; // Lombok @Getter, kein Setter } ``` So vermeidest du, dass jeder Command 5 Konstruktor-Parameter braucht. ## Tests Pro Command ein Testfall, der `GameContext` mit Mockito mockt (oder als Fake-IO baut): ```java @Test void goCommand_movesPlayerToConnectedRoom() { Room kitchen = ...; Room hallway = ...; kitchen.getExits().put(Direction.NORTH, hallway); Player p = new Player(kitchen, 0); GameContext ctx = new GameContext(world, p, new TestIO()); new GoCommand().execute(ctx, List.of("north")); assertThat(p.getCurrentRoom()).isEqualTo(hallway); } ```