392 lines
17 KiB
HTML
392 lines
17 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="de">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>Kapitel 11: Polymorphie & Interfaces</title>
|
||
<link rel="stylesheet" href="../assets/style.css">
|
||
<style>
|
||
/* Spezifische Styles für UML-Diagramme */
|
||
.uml-container {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
gap: 16px;
|
||
margin: 25px 0;
|
||
}
|
||
.uml-card {
|
||
background: #ffffff;
|
||
border: 2px solid var(--primary);
|
||
border-radius: var(--radius-sm);
|
||
width: 100%;
|
||
max-width: 380px;
|
||
box-shadow: var(--shadow-sm);
|
||
overflow: hidden;
|
||
font-family: var(--font-mono);
|
||
font-size: 0.85rem;
|
||
}
|
||
.uml-header {
|
||
background: var(--primary);
|
||
color: white;
|
||
text-align: center;
|
||
padding: 8px 12px;
|
||
font-weight: 700;
|
||
font-size: 0.95rem;
|
||
}
|
||
.uml-section {
|
||
padding: 8px 12px;
|
||
border-bottom: 1px solid var(--border-color);
|
||
color: #334155;
|
||
line-height: 1.5;
|
||
}
|
||
.uml-section:last-child {
|
||
border-bottom: none;
|
||
}
|
||
.uml-arrow {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
color: var(--primary);
|
||
font-weight: bold;
|
||
font-size: 1.1rem;
|
||
}
|
||
.uml-arrow::before {
|
||
content: '▲';
|
||
font-size: 1.2rem;
|
||
}
|
||
.uml-arrow-line {
|
||
width: 2px;
|
||
height: 20px;
|
||
background-color: var(--primary);
|
||
}
|
||
.uml-row {
|
||
display: flex;
|
||
gap: 16px;
|
||
justify-content: center;
|
||
width: 100%;
|
||
flex-wrap: wrap;
|
||
}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
|
||
<header>
|
||
<div class="header-container">
|
||
<nav class="nav-links">
|
||
<a href="../index.html">🏠 Zur Kursübersicht</a>
|
||
<span>›</span>
|
||
<a href="../10_vererbung_und_super/index.html">Kapitel 10 (Vererbung)</a>
|
||
<span>›</span>
|
||
<span style="color: white; font-weight: 600;">Kapitel 11</span>
|
||
</nav>
|
||
<div class="header-badge">Kapitel 11 • Schulabgleich 25.1</div>
|
||
<h1>Polymorphie & Interfaces: Viele Gestalten, ein Befehl 🎭📐</h1>
|
||
<p>Verstehe das Geheimnis der Vielgestaltigkeit: Gleicher Methodenname, unterschiedliches Verhalten und saubere Schnittstellen mit ABC und Duck Typing.</p>
|
||
</div>
|
||
</header>
|
||
|
||
<main class="container">
|
||
|
||
<!-- 1. Was ist Polymorphie? -->
|
||
<section class="card">
|
||
<h2>1. Was bedeutet Polymorphie? ("Vielgestaltigkeit")</h2>
|
||
|
||
<p>
|
||
Das Wort <strong>Polymorphie</strong> stammt aus dem Griechischen:
|
||
<br>
|
||
<code>poly</code> = <em>viel</em> | <code>morphe</code> = <em>Gestalt, Form</em> → <strong>"Vielgestaltigkeit"</strong>.
|
||
</p>
|
||
|
||
<p>
|
||
In der Objektorientierten Programmierung bedeutet das:
|
||
<strong>Verschiedene Klassen besitzen Methoden mit demselben Namen und derselben Signatur</strong>,
|
||
führen die Aufgabe im Hintergrund jedoch auf ihre ganz eigene Art und Weise aus.
|
||
</p>
|
||
|
||
<div class="box box-analogie">
|
||
<div class="box-title">📺 Die Universalfernbedienung-Analogie:</div>
|
||
Stelle dir eine Universal-Fernbedienung mit einer roten <strong>"Power"-Taste</strong> vor.
|
||
<br><br>
|
||
Wenn du auf "Power" drückst, sendet die Fernbedienung an alle Geräte den Befehl <code>einschalten()</code>:
|
||
<ul style="margin-left: 20px; margin-top: 8px;">
|
||
<li>Der <strong>Fernseher</strong> aktiviert sein OLED-Display.</li>
|
||
<li>Die <strong>Soundbar</strong> startet den Verstärker und verbindet Bluetooth.</li>
|
||
<li>Die <strong>Spielkonsole</strong> lädt das Betriebssystem und meldet den Controller an.</li>
|
||
</ul>
|
||
<br>
|
||
Die Fernbedienung muss nicht wissen, <em>wie</em> das Einschalten intern funktioniert – sie weiß nur, dass jedes Gerät eine Methode <code>einschalten()</code> besitzt!
|
||
</div>
|
||
</section>
|
||
|
||
<!-- 2. UML-Klassendiagramm -->
|
||
<section class="card">
|
||
<h2>2. 📊 UML-Klassendiagramm: Formen & Zeichenfläche</h2>
|
||
|
||
<p>
|
||
In unserem Praxisbeispiel ist <code>Form</code> ein <strong>Interface / eine abstrakte Basisklasse</strong>.
|
||
<code>Rechteck</code>, <code>Kreis</code> und <code>Dreieck</code> implementieren jeweils ihre eigene <code>flaeche()</code> und <code>umfang()</code>.
|
||
</p>
|
||
|
||
<div class="uml-container">
|
||
<!-- Abstrakte Basisklasse Form -->
|
||
<div class="uml-card">
|
||
<div class="uml-header">«abstract» Form</div>
|
||
<div class="uml-section">
|
||
- farbe: str = "schwarz"
|
||
</div>
|
||
<div class="uml-section">
|
||
+ __init__(farbe: str)<br>
|
||
+ <i>{abstract}</i> flaeche() -> float<br>
|
||
+ <i>{abstract}</i> umfang() -> float<br>
|
||
+ info() -> str
|
||
</div>
|
||
</div>
|
||
|
||
<div class="uml-arrow">
|
||
<div class="uml-arrow-line"></div>
|
||
</div>
|
||
|
||
<div class="uml-row">
|
||
<!-- Rechteck -->
|
||
<div class="uml-card" style="max-width: 270px;">
|
||
<div class="uml-header">Rechteck</div>
|
||
<div class="uml-section">
|
||
- breite: float<br>
|
||
- hoehe: float
|
||
</div>
|
||
<div class="uml-section">
|
||
+ flaeche() -> float<br>
|
||
+ umfang() -> float<br>
|
||
+ info() -> str
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Kreis -->
|
||
<div class="uml-card" style="max-width: 270px;">
|
||
<div class="uml-header">Kreis</div>
|
||
<div class="uml-section">
|
||
- radius: float
|
||
</div>
|
||
<div class="uml-section">
|
||
+ flaeche() -> float<br>
|
||
+ umfang() -> float<br>
|
||
+ info() -> str
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Dreieck -->
|
||
<div class="uml-card" style="max-width: 270px;">
|
||
<div class="uml-header">Dreieck</div>
|
||
<div class="uml-section">
|
||
- seite_a: float<br>
|
||
- seite_b: float
|
||
</div>
|
||
<div class="uml-section">
|
||
+ hypotenuse() -> float<br>
|
||
+ flaeche() -> float<br>
|
||
+ umfang() -> float<br>
|
||
+ info() -> str
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Zeichenflaeche Manager -->
|
||
<div class="uml-card" style="max-width: 450px; margin-top: 15px; border-color: var(--secondary);">
|
||
<div class="uml-header" style="background: var(--secondary);">Zeichenflaeche (Manager)</div>
|
||
<div class="uml-section">
|
||
- name: str<br>
|
||
- formen: list[Form]
|
||
</div>
|
||
<div class="uml-section">
|
||
+ hinzufuegen(form: Form) -> None<br>
|
||
+ gesamte_flaeche() -> float <i>(ruft form.flaeche() auf)</i><br>
|
||
+ gesamter_umfang() -> float <i>(ruft form.umfang() auf)</i><br>
|
||
+ formen_nach_farbe(farbe) -> list[Form]<br>
|
||
+ groesste_form() -> Form | None<br>
|
||
+ report() -> list[str]
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- 3. Wie funktioniert Polymorphie in Python? -->
|
||
<section class="card">
|
||
<h2>3. Wie funktioniert Polymorphie in Code?</h2>
|
||
|
||
<h3>Der Clou: Schleifen über gemischte Objekte ohne <code>if/else</code>!</h3>
|
||
|
||
<p>
|
||
Ohne Polymorphie müsstest du für jeden Typ eine eigene <code>if/elif</code>-Weiche schreiben:
|
||
</p>
|
||
|
||
<div class="code-container">
|
||
<div class="code-header">
|
||
<span>❌ Schlechter Stil: Starre Typ-Prüfungen</span>
|
||
</div>
|
||
<pre><span class="cm"># So bitte NICHT programmieren:</span>
|
||
<span class="kw">for</span> <span class="var">form</span> <span class="kw">in</span> <span class="var">formen</span>:
|
||
<span class="kw">if</span> <span class="fn">isinstance</span>(<span class="var">form</span>, <span class="fn">Rechteck</span>):
|
||
<span class="var">f</span> = <span class="var">form</span>.<span class="attr">breite</span> * <span class="var">form</span>.<span class="attr">hoehe</span>
|
||
<span class="kw">elif</span> <span class="fn">isinstance</span>(<span class="var">form</span>, <span class="fn">Kreis</span>):
|
||
<span class="var">f</span> = <span class="num">3.14159</span> * <span class="var">form</span>.<span class="attr">radius</span> ** <span class="num">2</span>
|
||
<span class="cm"># Bei jeder neuen Form (z.B. Dreieck) muss überall neuer Code eingefügt werden!</span></pre>
|
||
</div>
|
||
|
||
<p>
|
||
Mit <strong>Polymorphie</strong> rufen wir einfach dieselbe Methode auf allen Objekten auf – Python wählt automatisch die richtige Version:
|
||
</p>
|
||
|
||
<div class="code-container">
|
||
<div class="code-header">
|
||
<div class="code-dots">
|
||
<span class="code-dot dot-red"></span>
|
||
<span class="code-dot dot-yellow"></span>
|
||
<span class="code-dot dot-green"></span>
|
||
</div>
|
||
<span>✅ Eleganter Stil: Echte Polymorphie</span>
|
||
</div>
|
||
<pre><span class="var">formen</span> = [
|
||
<span class="fn">Rechteck</span>(<span class="num">4.0</span>, <span class="num">5.0</span>),
|
||
<span class="fn">Kreis</span>(<span class="num">3.0</span>),
|
||
<span class="fn">Dreieck</span>(<span class="num">3.0</span>, <span class="num">4.0</span>)
|
||
]
|
||
|
||
<span class="cm"># Ein einziger, eleganter Methodenaufruf für alle Formen:</span>
|
||
<span class="kw">for</span> <span class="var">form</span> <span class="kw">in</span> <span class="var">formen</span>:
|
||
<span class="fn">print</span>(<span class="str">f"{form.info()} -> Fläche: {form.flaeche():.2f}"</span>)</pre>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- 4. Abstrakte Basisklassen (ABC) & Duck Typing -->
|
||
<section class="card">
|
||
<h2>4. Abstrakte Basisklassen (<code>abc.ABC</code>) & Duck Typing</h2>
|
||
|
||
<div class="box box-tipp">
|
||
<div class="box-title">🦆 Was ist Duck Typing?</div>
|
||
<em>"If it walks like a duck and quacks like a duck, it's a duck."</em>
|
||
<br><br>
|
||
Python ist dynamisch typisiert: Solange ein Objekt eine Methode <code>flaeche()</code> besitzt, kann es auf der <code>Zeichenflaeche</code> platziert werden.
|
||
</div>
|
||
|
||
<h3>Verträge erzwingen mit <code>abc.ABC</code></h3>
|
||
<p>
|
||
Um sicherzustellen, dass Entwickler nicht vergessen, wichtige Methoden wie <code>flaeche()</code> zu schreiben, nutzen wir Pythons <code>abc</code>-Modul (<strong>A</strong>bstract <strong>B</strong>ase <strong>C</strong>lasses):
|
||
</p>
|
||
|
||
<div class="code-container">
|
||
<div class="code-header">
|
||
<span>Python (Abstrakte Methoden definieren)</span>
|
||
</div>
|
||
<pre><span class="kw">from</span> <span class="var">abc</span> <span class="kw">import</span> <span class="fn">ABC</span>, <span class="fn">abstractmethod</span>
|
||
|
||
<span class="kw">class</span> <span class="fn">Form</span>(<span class="fn">ABC</span>):
|
||
<span class="kw">def</span> <span class="fn">__init__</span>(<span class="var">self</span>, <span class="var">farbe</span>: <span class="fn">str</span> = <span class="str">"schwarz"</span>):
|
||
<span class="var">self</span>.<span class="attr">farbe</span> = <span class="var">farbe</span>
|
||
|
||
<span class="attr">@abstractmethod</span>
|
||
<span class="kw">def</span> <span class="fn">flaeche</span>(<span class="var">self</span>) -> <span class="fn">float</span>:
|
||
<span class="kw">pass</span> <span class="cm"># Jede Kindklasse MUSS diese Methode überschreiben!</span></pre>
|
||
</div>
|
||
|
||
<div class="box box-warning">
|
||
<div class="box-title">⚠️ Was passiert, wenn eine Kindklasse die abstrakte Methode vergisst?</div>
|
||
Wenn eine Kindklasse <code>flaeche()</code> nicht implementiert, verhindert Python das Erstellen von Objekten mit einem klaren Fehler:
|
||
<br>
|
||
<code>TypeError: Can't instantiate abstract class MeineForm with abstract method flaeche</code>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- 5. Aufgaben -->
|
||
<section class="card">
|
||
<h2>5. 🎯 Deine Aufgaben in <code>aufgabe.py</code></h2>
|
||
|
||
<p>Öffne die Datei <code>11_polymorphie_und_interfaces/aufgabe.py</code> und vervollständige das System:</p>
|
||
|
||
<ul class="task-list">
|
||
<li class="task-item">
|
||
<input type="checkbox" class="task-checkbox" id="t11_1">
|
||
<label for="t11_1" class="task-label">
|
||
<strong>TODO 1–4 (Abstrakte Basisklasse Form):</strong> Konstruktor mit <code>farbe="schwarz"</code>, abstrakte Methoden <code>@abstractmethod flaeche()</code> und <code>@abstractmethod umfang()</code> sowie gemeinsame <code>info()</code>.
|
||
</label>
|
||
</li>
|
||
<li class="task-item">
|
||
<input type="checkbox" class="task-checkbox" id="t11_2">
|
||
<label for="t11_2" class="task-label">
|
||
<strong>TODO 5–8 (Rechteck):</strong> Erbt von <code>Form</code>. Speichert <code>breite</code> und <code>hoehe</code>, implementiert <code>flaeche()</code> (<code>breite * hoehe</code>) und <code>umfang()</code> (<code>2 * (breite + hoehe)</code>).
|
||
</label>
|
||
</li>
|
||
<li class="task-item">
|
||
<input type="checkbox" class="task-checkbox" id="t11_3">
|
||
<label for="t11_3" class="task-label">
|
||
<strong>TODO 9–12 (Kreis):</strong> Erbt von <code>Form</code>. Speichert <code>radius</code>, implementiert <code>flaeche()</code> (<code>math.pi * radius**2</code>) und <code>umfang()</code> (<code>2 * math.pi * radius</code>).
|
||
</label>
|
||
</li>
|
||
<li class="task-item">
|
||
<input type="checkbox" class="task-checkbox" id="t11_4">
|
||
<label for="t11_4" class="task-label">
|
||
<strong>TODO 13–17 (Dreieck):</strong> Rechtwinkliges Dreieck mit Katheten <code>seite_a</code> und <code>seite_b</code>. Berechnet <code>hypotenuse()</code> mit Pythagoras (<code>math.sqrt(a**2 + b**2)</code>), <code>flaeche()</code> und <code>umfang()</code>.
|
||
</label>
|
||
</li>
|
||
<li class="task-item">
|
||
<input type="checkbox" class="task-checkbox" id="t11_5">
|
||
<label for="t11_5" class="task-label">
|
||
<strong>TODO 18–25 (Manager-Klasse Zeichenflaeche):</strong> Verwaltet eine polymorphe Liste von Formen. Berechnet <code>gesamte_flaeche()</code>, <code>gesamter_umfang()</code>, filtert mit <code>formen_nach_farbe(farbe)</code> und ermittelt die <code>groesste_form()</code>.
|
||
</label>
|
||
</li>
|
||
</ul>
|
||
|
||
<h3>💡 Lösungshinweise (Aufklappen bei Bedarf)</h3>
|
||
|
||
<details>
|
||
<summary>🔍 Tipp zu TODO 14 (Hypotenuse berechnen)</summary>
|
||
<div class="details-content">
|
||
<div class="code-container">
|
||
<pre><span class="kw">def</span> <span class="fn">hypotenuse</span>(<span class="var">self</span>) -> <span class="fn">float</span>:
|
||
<span class="kw">return</span> <span class="var">math</span>.<span class="fn">sqrt</span>(<span class="var">self</span>.<span class="attr">seite_a</span> ** <span class="num">2</span> + <span class="var">self</span>.<span class="attr">seite_b</span> ** <span class="num">2</span>)</pre>
|
||
</div>
|
||
</div>
|
||
</details>
|
||
|
||
<details>
|
||
<summary>🔍 Tipp zu TODO 21 (Polymorphe Gesamtfläche)</summary>
|
||
<div class="details-content">
|
||
<div class="code-container">
|
||
<pre><span class="kw">def</span> <span class="fn">gesamte_flaeche</span>(<span class="var">self</span>) -> <span class="fn">float</span>:
|
||
<span class="kw">return</span> <span class="fn">sum</span>(<span class="var">form</span>.<span class="fn">flaeche</span>() <span class="kw">for</span> <span class="var">form</span> <span class="kw">in</span> <span class="var">self</span>.<span class="attr">formen</span>)</pre>
|
||
</div>
|
||
</div>
|
||
</details>
|
||
|
||
<details>
|
||
<summary>🔍 Tipp zu TODO 23 (Farben filtern ohne Groß-/Kleinschreibung)</summary>
|
||
<div class="details-content">
|
||
<div class="code-container">
|
||
<pre><span class="kw">def</span> <span class="fn">formen_nach_farbe</span>(<span class="var">self</span>, <span class="var">farbe</span>: <span class="fn">str</span>) -> <span class="fn">list</span>[<span class="fn">Form</span>]:
|
||
<span class="kw">return</span> [<span class="var">f</span> <span class="kw">for</span> <span class="var">f</span> <span class="kw">in</span> <span class="var">self</span>.<span class="attr">formen</span> <span class="kw">if</span> <span class="var">f</span>.<span class="attr">farbe</span>.<span class="fn">lower</span>() == <span class="var">farbe</span>.<span class="fn">lower</span>()]</pre>
|
||
</div>
|
||
</div>
|
||
</details>
|
||
</section>
|
||
|
||
<!-- 6. Testen & Navigation -->
|
||
<section class="card">
|
||
<h2>6. 🧪 Lösung überprüfen</h2>
|
||
<p>Führe im Terminal im Ordner <code>11_polymorphie_und_interfaces</code> folgenden Befehl aus:</p>
|
||
|
||
<div class="code-container">
|
||
<div class="code-header">
|
||
<span>Terminal</span>
|
||
</div>
|
||
<pre><code>python3 test_aufgabe.py</code></pre>
|
||
</div>
|
||
|
||
<div class="footer-nav">
|
||
<a href="../10_vererbung_und_super/index.html" class="btn btn-secondary">← Zurück zu Kapitel 10</a>
|
||
<a href="../12_exceptions_und_fehlerbehandlung/index.html" class="btn">Weiter zu Kapitel 12 (Exceptions) →</a>
|
||
</div>
|
||
</section>
|
||
|
||
</main>
|
||
|
||
</body>
|
||
</html>
|