overpass/TASK.md

2.5 KiB

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:

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:
   class StorageType(StrEnum):
       JSON     = "json"
       POSTGRES = "postgres"
  1. 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.
  2. Passe config.yaml an — der storage-Abschnitt bekommt ein type-Feld:
   storage:
     type: json
     params:
       output_dir: ./data/results
  1. Ersetze in main.py die direkte Instanziierung durch:
   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?