Files
Jander_Semester2/Semesterprojekt/docs/architecture.md
Jean-Luc Makiola 83643a192f semesterprojekt: implement full text adventure (phases 1-7)
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.
2026-05-25 21:37:59 +02:00

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.