# Kapitel 16: Master-Abschlussprojekt – PetCare & Tierheim-Manager 🐾πŸ₯πŸ–₯️ Herzlichen GlΓΌckwunsch! Du bist am **großen Finale der gesamten Python OOP-Übungsreihe** angekommen. Hier fΓΌhrst du alles zusammen, was du in den drei Lehrpfaden gelernt hast – von Datentypen und Schleifen ΓΌber Vererbung, Polymorphie, Exceptions und JSON-Persistenz bis hin zur interaktiven **Tkinter Desktop-Applikation** mit professioneller **Model-View-Controller (MVC)** Architektur. --- ## 🧭 Die 3 Lehrpfade im Masterprojekt vereint ``` [ πŸ† KAPITEL 16: MEISTERSTÜCK ] β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β–Ό β–Ό β–Ό [ πŸ›£οΈ LEHRPFAD 1: GRUNDLAGEN ] [ πŸ›£οΈ LEHRPFAD 2: OOP & TOOLS ] [ πŸ›£οΈ LEHRPFAD 3: SOFTWARETECHNIK ] β€’ Datentypen, Strings & Mathe β€’ Klassen, __init__, self β€’ Vererbung & super() β€’ Schleifen, Listen & Dicts β€’ Methoden & Interaktionen β€’ Polymorphie & Interfaces β€’ Typsichere Validierung β€’ __str__ & ReprΓ€sentation β€’ Eigene Exception-Klassen β€’ CSV-Tabellenexport β€’ Modularer Aufbau β€’ JSON Savegame-Persistenz β€’ Tkinter Desktop-GUI (MVC) β€’ 100% TDD / Unittest-Suite ``` --- ## πŸ›οΈ Die Architektur: Model-View-Controller (MVC) In professioneller Software wird die **GeschΓ€ftslogik (Model)** strikt von der **grafischen OberflΓ€che (View & Controller)** getrennt. Dadurch kann die gesamte Fachlogik zu 100% in automatischen Unittests ohne Bildschirm/Fenster getestet werden! ``` β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ BENUTZER β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–²β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ 1. Klickt Button / gibt Text einβ”‚ 4. Sieht Update β–Ό β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ VIEW & CONTROLLER (TierheimApp) β”‚ β”‚ β€’ Tkinter-Fenster, Formular, Listbox, Aktionsbuttons, Statusleiste β”‚ β”‚ β€’ Liest Formulareingaben aus & fΓ€ngt ValidierungsFehler ab β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–²β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ 2. Ruft Methoden auf β”‚ 3. Liefert Daten β–Ό β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ MODEL (Tierheim) β”‚ β”‚ β€’ Verwaltet Tierbestand (Hund, Katze, Vogel) β”‚ β”‚ β€’ Filter (Art, ungeimpft, hungrig) & Statistiken (Ø Alter, Gewicht) β”‚ β”‚ β€’ Persistenz: JSON Speichern/Laden & CSV-Export β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ ``` --- ## πŸ“Š UML-Klassendiagramm ``` β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ Exception: TierheimFehler β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–²β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ ValidierungsFehler β”‚ β”‚TierNichtGefunden β”‚ β”‚KapazitaetUeberschritten β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ Β«classΒ» β”‚ β”‚ Tier β”‚ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ β”‚ + name: str β”‚ β”‚ + alter: int β”‚ β”‚ + gewicht: float β”‚ β”‚ + geimpft: bool β”‚ β”‚ + hunger: int (0..100) β”‚ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ β”‚ + fuettern(menge_g: int): str β”‚ β”‚ + impfen(): bool β”‚ β”‚ + mache_laut(): str β”‚ β”‚ + get_details(): str β”‚ β”‚ + to_dict(): dict β”‚ β”‚ + from_dict(data: dict): Tier β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–²β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ (Vererbung) β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ Hund β”‚ β”‚ Katze β”‚ β”‚ Vogel β”‚ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ β”‚ + rasse: str β”‚ β”‚ + stubenrein: bool β”‚ β”‚ + spannweite_cm: float β”‚ β”‚ + gassigegangen: bool β”‚ β”‚ + kratzbaum_benutzt: bool β”‚ β”‚ + kann_sprechen: bool β”‚ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ β”‚ + gassi_gehen(minuten): str β”‚ β”‚ + kratzen(): str β”‚ β”‚ + fliegen(runden): str β”‚ β”‚ + mache_laut(): str (Wuff!) β”‚ β”‚ + mache_laut(): str (Miau!) β”‚ β”‚ + mache_laut(): str (Tsch!) β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β–² β”‚ 1:n Komposition β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ Tierheim β”‚ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ β”‚ + name: str β”‚ β”‚ + max_kapazitaet: int β”‚ β”‚ + tiere: List[Tier] β”‚ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ β”‚ + tier_aufnehmen(tier: Tier): None β”‚ β”‚ + tier_entlassen(name: str): Tier β”‚ β”‚ + finde_tier(name: str): Optional[Tier] β”‚ β”‚ + filtriere_nach_art(art: str): List[Tier] β”‚ β”‚ + ungeimpfte_tiere(): List[Tier] β”‚ β”‚ + hungrige_tiere(schwellenwert: int): List[Tier] β”‚ β”‚ + durchschnittsalter(): float β”‚ β”‚ + gesamtgewicht(): float β”‚ β”‚ + alle_fuettern(menge: int): dict β”‚ β”‚ + alle_impfen(): int β”‚ β”‚ + speichern_json(pfad: str): None β”‚ β”‚ + laden_json(pfad: str): None β”‚ β”‚ + exportiere_csv(pfad: str): None β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ ``` --- ## πŸ“ Aufgabenstellung (Schritt fΓΌr Schritt) Γ–ffne die Datei `aufgabe.py` und bearbeite die nummerierten Abschnitte: ### Schritt 1: Eigene Exceptions definieren Erstelle die Exception-Klassen: - `TierheimFehler(Exception)` als Basis - `ValidierungsFehler(TierheimFehler)` - `TierNichtGefundenFehler(TierheimFehler)` - `KapazitaetUeberschrittenFehler(TierheimFehler)` ### Schritt 2: Basisklasse `Tier` implementieren - Validiere im `__init__`: `name` darf nicht leer sein, `alter >= 0`, `gewicht > 0`. - Implementiere `fuettern(futtermenge_gramm)`: Senkt Hunger um `futtermenge_gramm * 0.2` (Minimum 0). - Implementiere `impfen()`: Setzt `geimpft = True` (gibt `True` bei Neugeimpften, `False` bei bereits Geimpften). - Implementiere `to_dict()` und die Factory-Methode `from_dict(cls, data)` (stellt automatisch die korrekte Kindklasse wieder her!). ### Schritt 3: Spezialisierte Kindklassen `Hund`, `Katze`, `Vogel` - Nutze `super().__init__(...)` fΓΌr gemeinsame Attribute. - Überschreibe `mache_laut()` polymorph (Hund bellt, Katze miaut/schnurrt, Vogel zwitschert/plappert). - Implementiere artspezifische Methoden (`gassi_gehen()`, `kratzen()`, `fliegen()`). - Erweitere `to_dict()` um die Kindklassen-Attribute. ### Schritt 4: Das Model `Tierheim` - Aufnahme von Tieren mit KapazitΓ€tsprΓΌfung (`max_kapazitaet`). - Entlassen von Tieren nach Name (mit Exception-Handling falls nicht gefunden). - Filterfunktionen (`filtriere_nach_art`, `ungeimpfte_tiere`, `hungrige_tiere`). - Statistiken: `durchschnittsalter()`, `gesamtgewicht()`. - Massenaktionen: `alle_fuettern()`, `alle_impfen()`. - Persistenz: `speichern_json()`, `laden_json()` und `exportiere_csv()`. ### Schritt 5: Die Desktop-GUI `TierheimApp` (Tkinter) - VerknΓΌpfe Buttons und Formularfelder mit den Model-Methoden. - Fange Fehler (`ValidierungsFehler`, etc.) in `try-except`-BlΓΆcken ab und gib Feedback in der dynamischen Statusleiste. - Halte die Listbox und KapazitΓ€tsanzeige synchron mit dem Datenmodell. --- ## πŸ§ͺ Tests ausfΓΌhren & App starten ### 1. Automatische Unittests ausfΓΌhren: ```bash python3 test_aufgabe.py ``` ### 2. Desktop-App mit grafischer OberflΓ€che starten: ```bash python3 app.py ``` --- ## πŸ’‘ Best Practices fΓΌr dein MeisterstΓΌck 1. **Never trust user input:** PrΓΌfe Datentypen und Grenzwerte frΓΌhzeitig im Konstruktor. 2. **Polymorphie nutzen:** Anstatt mit vielen `if/elif`-Abfragen nach dem Typ zu unterscheiden, rufe einfach `tier.mache_laut()` oder `tier.fuettern()` auf! 3. **Model & View trennen:** Halte Berechnungen und Daten im `Tierheim`-Objekt und nutze Tkinter nur zur Anzeige.