Walking skeleton through Swing GUI: YAML-driven world (4 rooms, 4 items, 1 NPC), HashMap command dispatch with parser, three-tier item hierarchy (readable / switchable / plain), and end-to-end NPC give/receive flow. 67 tests green.
94 lines
3.0 KiB
Markdown
94 lines
3.0 KiB
Markdown
# Architektur
|
|
|
|
## Schichten
|
|
|
|
```mermaid
|
|
flowchart TD
|
|
IO["io (Konsole / GUI)<br/>Interaktion mit Spieler"]
|
|
GAME["game (Engine/Loop)<br/>Spielfluss, hält World + Player"]
|
|
CMD["command (Commands)<br/>go, take, use, talk, ..."]
|
|
MODEL["model (Domain)<br/>Room, Item, Player, Npc, World"]
|
|
LOADER["loader (YAML + DTO)<br/>liest Ressourcen, validiert"]
|
|
|
|
IO -- "liest/schreibt via GameIO" --> GAME
|
|
GAME -- "dispatcht via CommandRegistry" --> CMD
|
|
CMD -- "mutiert" --> MODEL
|
|
LOADER -- "baut auf aus DTOs" --> MODEL
|
|
```
|
|
|
|
## Package-Struktur
|
|
|
|
```mermaid
|
|
flowchart LR
|
|
root["thb.jeanluc.adventure"]
|
|
|
|
root --> app["App.java<br/>AppGui.java"]
|
|
root --> model
|
|
root --> loader
|
|
root --> command
|
|
root --> game
|
|
root --> io
|
|
|
|
model --> model_files["World, Room,<br/>Player, Npc, Direction"]
|
|
model --> item
|
|
item --> item_files["Item (abstract)<br/>ReadableItem<br/>SwitchableItem<br/>PlainItem"]
|
|
|
|
loader --> loader_files["WorldLoader<br/>ReferenceResolver<br/>WorldValidator"]
|
|
loader --> dto
|
|
dto --> dto_files["RoomDto, ItemDto,<br/>NpcDto, GameDto"]
|
|
|
|
command --> cmd_core["Command (Interface)<br/>CommandRegistry<br/>CommandParser"]
|
|
command --> impl
|
|
impl --> impl_files["GoCommand, TakeCommand,<br/>DropCommand, UseCommand,<br/>InventoryCommand, LookCommand,<br/>TalkCommand, GiveCommand,<br/>HelpCommand, QuitCommand"]
|
|
|
|
game --> game_files["Game (Loop)<br/>GameContext"]
|
|
|
|
io --> io_files["GameIO (Interface)<br/>ConsoleIO<br/>SwingIO"]
|
|
```
|
|
|
|
## DTO vs. Domain-Trennung
|
|
|
|
**Kernprinzip:** YAML wird in Records deserialisiert (DTOs), erst danach werden String-IDs zu Objekt-Referenzen aufgelöst.
|
|
|
|
| | DTO | Domain |
|
|
|---|---|---|
|
|
| Typ | `record` | Lombok-Klasse |
|
|
| Mutabilität | immutable | mutable wo nötig |
|
|
| Felder | nur Daten + String-IDs | Objekt-Referenzen, EnumMap, etc. |
|
|
| Zweck | Jackson-Mapping | Spielablauf |
|
|
| Tests | Loader-Tests | Domain-Tests, brauchen kein YAML |
|
|
|
|
**Warum getrennt?**
|
|
- Domain-Klassen müssen nicht mit Jackson-Annotations verschmutzt werden
|
|
- `Room.exits` ist `EnumMap<Direction, Room>` (typisicher, schnell) statt `Map<String, String>` (was zum YAML passt)
|
|
- Validierung passiert beim Übergang DTO→Domain (siehe [loading-flow.md](loading-flow.md))
|
|
- Domain-Tests können Objekte direkt im Code bauen, ohne YAML-Fixtures
|
|
|
|
## Game-Loop (vereinfacht)
|
|
|
|
```java
|
|
while (!game.isOver()) {
|
|
String input = io.read();
|
|
ParsedCommand parsed = parser.parse(input);
|
|
Command cmd = registry.get(parsed.verb());
|
|
if (cmd == null) {
|
|
io.write("Unbekannter Befehl.");
|
|
} else {
|
|
cmd.execute(context, parsed.args());
|
|
}
|
|
}
|
|
```
|
|
|
|
## IO-Abstraktion
|
|
|
|
Konsole und GUI teilen sich `GameIO`:
|
|
|
|
```java
|
|
public interface GameIO {
|
|
String read(); // blockierende Leseoperation
|
|
void write(String text);
|
|
}
|
|
```
|
|
|
|
Damit ist der Game-Loop **identisch** für beide Modi. `SwingIO` blockiert intern mit einer `BlockingQueue<String>`, die vom JTextField-ActionListener gefüllt wird.
|