it-praxisportal/11_polymorphie_und_interfaces/index.html

392 lines
17 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!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>&rsaquo;</span>
<a href="../10_vererbung_und_super/index.html">Kapitel 10 (Vererbung)</a>
<span>&rsaquo;</span>
<span style="color: white; font-weight: 600;">Kapitel 11</span>
</nav>
<div class="header-badge">Kapitel 11 &bull; Schulabgleich 25.1</div>
<h1>Polymorphie &amp; 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> &nbsp;|&nbsp; <code>morphe</code> = <em>Gestalt, Form</em> &rarr; <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 &amp; 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">&laquo;abstract&raquo; 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 &nbsp;<i>(ruft form.flaeche() auf)</i><br>
+ gesamter_umfang() -> float &nbsp;<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>) &amp; 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">&larr; Zurück zu Kapitel 10</a>
<a href="../12_exceptions_und_fehlerbehandlung/index.html" class="btn">Weiter zu Kapitel 12 (Exceptions) &rarr;</a>
</div>
</section>
</main>
</body>
</html>