Kapitel 11 • Schulabgleich 25.1

Polymorphie & Interfaces: Viele Gestalten, ein Befehl 🎭📐

Verstehe das Geheimnis der Vielgestaltigkeit: Gleicher Methodenname, unterschiedliches Verhalten und saubere Schnittstellen mit ABC und Duck Typing.

📂 Dateien dieser Einheit:
💻 In Web-IDE öffnen → 📄 aufgabe.py (Startercode) 🧪 test_aufgabe.py (Tests) 💡 musterloesung.py (Lösung) 📖 README.md

1. Was bedeutet Polymorphie? ("Vielgestaltigkeit")

Das Wort Polymorphie stammt aus dem Griechischen:
poly = viel  |  morphe = Gestalt, Form → "Vielgestaltigkeit".

In der Objektorientierten Programmierung bedeutet das: Verschiedene Klassen besitzen Methoden mit demselben Namen und derselben Signatur, führen die Aufgabe im Hintergrund jedoch auf ihre ganz eigene Art und Weise aus.

📺 Die Universalfernbedienung-Analogie:
Stelle dir eine Universal-Fernbedienung mit einer roten "Power"-Taste vor.

Wenn du auf "Power" drückst, sendet die Fernbedienung an alle Geräte den Befehl einschalten():
  • Der Fernseher aktiviert sein OLED-Display.
  • Die Soundbar startet den Verstärker und verbindet Bluetooth.
  • Die Spielkonsole lädt das Betriebssystem und meldet den Controller an.

Die Fernbedienung muss nicht wissen, wie das Einschalten intern funktioniert – sie weiß nur, dass jedes Gerät eine Methode einschalten() besitzt!

2. 📊 UML-Klassendiagramm: Formen & Zeichenfläche

In unserem Praxisbeispiel ist Form ein Interface / eine abstrakte Basisklasse. Rechteck, Kreis und Dreieck implementieren jeweils ihre eigene flaeche() und umfang().

«abstract» Form
- farbe: str = "schwarz"
+ __init__(farbe: str)
+ {abstract} flaeche() -> float
+ {abstract} umfang() -> float
+ info() -> str
Rechteck
- breite: float
- hoehe: float
+ flaeche() -> float
+ umfang() -> float
+ info() -> str
Kreis
- radius: float
+ flaeche() -> float
+ umfang() -> float
+ info() -> str
Dreieck
- seite_a: float
- seite_b: float
+ hypotenuse() -> float
+ flaeche() -> float
+ umfang() -> float
+ info() -> str
Zeichenflaeche (Manager)
- name: str
- formen: list[Form]
+ hinzufuegen(form: Form) -> None
+ gesamte_flaeche() -> float  (ruft form.flaeche() auf)
+ gesamter_umfang() -> float  (ruft form.umfang() auf)
+ formen_nach_farbe(farbe) -> list[Form]
+ groesste_form() -> Form | None
+ report() -> list[str]

3. Wie funktioniert Polymorphie in Code?

Der Clou: Schleifen über gemischte Objekte ohne if/else!

Ohne Polymorphie müsstest du für jeden Typ eine eigene if/elif-Weiche schreiben:

❌ Schlechter Stil: Starre Typ-Prüfungen
# So bitte NICHT programmieren:
for form in formen:
    if isinstance(form, Rechteck):
        f = form.breite * form.hoehe
    elif isinstance(form, Kreis):
        f = 3.14159 * form.radius ** 2
    # Bei jeder neuen Form (z.B. Dreieck) muss überall neuer Code eingefügt werden!

Mit Polymorphie rufen wir einfach dieselbe Methode auf allen Objekten auf – Python wählt automatisch die richtige Version:

✅ Eleganter Stil: Echte Polymorphie
formen = [
    Rechteck(4.0, 5.0),
    Kreis(3.0),
    Dreieck(3.0, 4.0)
]

# Ein einziger, eleganter Methodenaufruf für alle Formen:
for form in formen:
    print(f"{form.info()} -> Fläche: {form.flaeche():.2f}")

4. Abstrakte Basisklassen (abc.ABC) & Duck Typing

🦆 Was ist Duck Typing?
"If it walks like a duck and quacks like a duck, it's a duck."

Python ist dynamisch typisiert: Solange ein Objekt eine Methode flaeche() besitzt, kann es auf der Zeichenflaeche platziert werden.

Verträge erzwingen mit abc.ABC

Um sicherzustellen, dass Entwickler nicht vergessen, wichtige Methoden wie flaeche() zu schreiben, nutzen wir Pythons abc-Modul (Abstract Base Classes):

Python (Abstrakte Methoden definieren)
from abc import ABC, abstractmethod

class Form(ABC):
    def __init__(self, farbe: str = "schwarz"):
        self.farbe = farbe

    @abstractmethod
    def flaeche(self) -> float:
        pass   # Jede Kindklasse MUSS diese Methode überschreiben!
⚠️ Was passiert, wenn eine Kindklasse die abstrakte Methode vergisst?
Wenn eine Kindklasse flaeche() nicht implementiert, verhindert Python das Erstellen von Objekten mit einem klaren Fehler:
TypeError: Can't instantiate abstract class MeineForm with abstract method flaeche

5. 🎯 Deine Aufgaben in aufgabe.py

Öffne die Datei aufgabe.py und vervollständige das System:

💡 Lösungshinweise (Aufklappen bei Bedarf)

🔍 Tipp zu TODO 14 (Hypotenuse berechnen)
def hypotenuse(self) -> float:
    return math.sqrt(self.seite_a ** 2 + self.seite_b ** 2)
🔍 Tipp zu TODO 21 (Polymorphe Gesamtfläche)
def gesamte_flaeche(self) -> float:
    return sum(form.flaeche() for form in self.formen)
🔍 Tipp zu TODO 23 (Farben filtern ohne Groß-/Kleinschreibung)
def formen_nach_farbe(self, farbe: str) -> list[Form]:
    return [f for f in self.formen if f.farbe.lower() == farbe.lower()]

6. 🧪 Lösung überprüfen

Führe im Terminal im Ordner 11_polymorphie_und_interfaces folgenden Befehl aus:

Terminal
python3 test_aufgabe.py

🧠 Diagnostischer Selbst-Check

Überprüfe dein Verständnis der Kernkonzepte und typischen Denkfallen, bevor du den Code in der Web-IDE schreibst.