# Task 13 — Storage-Factory und StorageType ## Rückblick Task 12: Storage-Abstraktion Ihr habt `storage.py` mit einer abstrakten Basisklasse `Storage` und einer konkreten `JsonStorage` eingeführt. Die wichtigsten Punkte: - **ABC und `@abstractmethod`:** Eine Klasse, die von `ABC` erbt und eine `@abstractmethod` definiert, kann nicht direkt instanziiert werden — Python wirft einen `TypeError`. Eine Unterklasse muss *alle* abstrakten Methoden implementieren, sonst gilt sie selbst als abstrakt. - **Warum `store()` einen `str` zurückgibt:** Der Aufrufer bekommt einen Identifier zurück (Dateipfad, Tabellenname, URL), ohne zu wissen, welches Backend dahintersteckt. Das ist nützlich fürs Logging und für Tests. - **`StorageError`:** Eine eigene Exception macht den Code robuster — `main.py` muss nur `StorageError` kennen, nicht alle möglichen `OSError`-, `psycopg2`- oder sonstigen Backend-Fehler. ## Aufgabe In `main.py` steht aktuell: ```python storage = JsonStorage(output_dir=config["storage"]["output_dir"]) ``` Das bedeutet: Will man später ein anderes Backend verwenden, muss `main.py` angefasst werden. Ausserdem muss `main.py` wissen, welche Klasse (`JsonStorage`, `PostgresStorage`, ...) zu welchem Config-Wert gehört. Ziel ist eine **Factory-Funktion** `build_storage(cfg)`, die diese Entscheidung übernimmt — `main.py` übergibt nur die Config und bekommt ein fertiges `Storage`-Objekt zurück. **Konkret:** 1. Füge in `storage.py` eine `StorageType`-Enum hinzu: ```python class StorageType(StrEnum): JSON = "json" POSTGRES = "postgres" ``` 2. Schreibe eine Factory-Funktion `build_storage(cfg: dict) -> Storage` in `storage.py`, die anhand von `cfg["type"]` den richtigen Storage instanziiert. Verwende dafür ein `match`-Statement. 3. Passe `config.yaml` an — der `storage`-Abschnitt bekommt ein `type`-Feld: ```yaml storage: type: json params: output_dir: ./data/results ``` 4. Ersetze in `main.py` die direkte Instanziierung durch: ```python storage = build_storage(config["storage"]) ``` **Fragen zum Nachdenken:** - Was ist der Vorteil eines `match`-Statements gegenüber einer `if/elif`-Kette — und ab wann lohnt sich das? - Was passiert, wenn jemand in `config.yaml` einen ungültigen `type`-Wert einträgt (z.B. `"mongodb"`)? Wie sollte `build_storage()` damit umgehen? - Warum übergeben wir `params` als `**params` an den Konstruktor — und was ist der Vorteil gegenüber einzelnen Parametern?