Deklarative Programmierung in V0.2
Paradigmenwechsel: Von Imperativ zu Deklarativ CharGraph V0.2 führt einen fundamentalen Paradigmenwechsel ein: Deklarative Programmierung statt imperative if-else-Kaskaden. Lernziel Schüler und Studenten verstehen:
Den Unterschied zwischen imperativem und deklarativem Code Vorteile der Trennung von Daten und Logik Rule-Engine-Pattern in modernen Softwaresystemen Wartbarkeit und Testbarkeit durch Modularisierung
Was ist deklarative Programmierung? Imperativ: "WIE etwas gemacht wird" (Ablauf, Schritt-für-Schritt) Deklarativ: "WAS erreicht werden soll" (Ziel, Beschreibung) Vergleich am Beispiel Aufgabe: Finde alle geraden Zahlen in einer Liste und verdoppele sie.
Imperativ (JavaScript)
let zahlen = [1, 2, 3, 4, 5, 6]; let ergebnis = [];
for(let i = 0; i < zahlen.length; i++) { // WIE: Schleife if(zahlen[i] % 2 === 0) { // WIE: Bedingung ergebnis.push(zahlen[i] * 2); // WIE: Operation } } // ergebnis = [4, 8, 12]
Deklarativ (JavaScript)
let zahlen = [1, 2, 3, 4, 5, 6]; let ergebnis = zahlen .filter(n => n % 2 === 0) // WAS: Nur gerade Zahlen .map(n => n * 2); // WAS: Verdoppeln // ergebnis = [4, 8, 12]
CharGraph V0.1 vs. V0.2 V0.1: Imperativer Ansatz (285 Zeilen if-else) Problem: Zeit in Wörter umwandeln function getTimeWords(hh, mm) { let words = ["ES", "IST"];
// 285 Zeilen verschachtelte if-else-Statements if(mm === 0) { words.push(hours[hh % 12]); if(hasUhr) words.push("UHR"); } else if(mm === 1) { words.push("NACH", hours[hh % 12]); } else if(mm === 2) { if(hasKurz) { words.push("KURZ", "NACH", hours[hh % 12]); } else { words.push("NACH", hours[hh % 12]); } } else if(mm === 3) { if(hasBald && hasFast) { words.push("BALD", "FÜNF", "NACH", hours[hh % 12]); } else if(hasBald) { // ... } // ... weitere 55 else-if Blöcke }
return words; }
Probleme
- ❌ Schwer lesbar: Man muss den gesamten Code durchgehen
- ❌ Schwer wartbar: Änderung an :29 kann :30 beeinflussen
- ❌ Schwer testbar: Viele Pfade, schwer zu isolieren
- ❌ Fehleranfällig: Copy-Paste-Fehler, vergessene Fälle V0.2: Deklarativer Ansatz (60 Regel-Objekte) Lösung: Daten-getriebene Regel-Engine // Datenstruktur: Array von Regel-Objekten const MINUTE_RULES = [ // Regel 0: Volle Stunde { range: [0, 0], handler: (ctx) => [ctx.hourWord, ...(ctx.hasUhrAtEnd ? ["UHR"] : [])] },
// Regel 1-2: NACH { range: [1, 2], handler: (ctx) => { if(ctx.mm === 2 && ctx.hasKurz) { return ["KURZ", "NACH", ctx.hourWord]; } return ["NACH", ctx.hourWord]; } },
// Regel 3-4: BALD FÜNF NACH { range: [3, 4], handler: (ctx) => { // Priorität 1: FAST (mit Validierung!) if(ctx.hasFast && ctx.mm === 4) { const test = ["FAST", "FÜNF", "NACH", ctx.hourWord]; if(validate(test)) return test; } // Priorität 2: BALD if(ctx.hasBald) { return ["BALD", "FÜNF", "NACH", ctx.hourWord]; } // Fallback return ["NACH", ctx.hourWord]; } },
// ... 57 weitere Regeln ];
// Einfache Lookup-Funktion function getTimeWords(hh, mm) { const rule = MINUTE_RULES.find(r => mm >= r.range[0] && mm <= r.range[1] );
const context = buildContext(hh, mm); return ["ES", "IST", ...rule.handler(context)]; }
Vorteile
- ✅ Selbstdokumentierend: Jede Regel beschreibt einen Zeitpunkt
- ✅ Isolation: Änderung an :29 betrifft nur diese Regel
- ✅ Testbar: Jede Regel einzeln testbar
- ✅ Erweiterbar: Neue Regel? Einfach hinzufügen! Visualisierung: Zeit-Mapping
Die Grafik zeigt die deklarative Struktur:
Jeder Pfeil = eine deklarative Regel X-Achse = Minuten (0-59) Y-Achse = Sprachmuster
Farben
- Grün: Standard-Zeitangaben (FÜNF NACH, ZEHN VOR)
- Rot: Modifikatoren (FAST, BALD)
- Orange: Spezialfälle (HALB-Zeiten)
Didaktischer Wert
Für Schüler (Sek I + II) Unterrichtseinheit: "Deklarative vs. Imperative Programmierung"
Einstieg: Vergleich beider Ansätze an einfachem Beispiel Vertiefung: CharGraph V0.1 vs. V0.2 Code-Analyse Praxis: Eigene Regel für :XX hinzufügen Reflexion: Wartbarkeit, Testbarkeit diskutieren
Lernziele
- Verstehen, wann welcher Ansatz sinnvoll ist
- Erkennen von Code-Smells (zu viele if-else)
- Refactoring-Techniken anwenden
- Design Patterns verstehen (Rule Engine, Strategy Pattern) Für Studenten (Hochschule)
Vertiefung
Software-Engineering: Wartbarkeitsmetriken (Zyklomatische Komplexität) Design Patterns: Rule Engine, Strategy, Chain of Responsibility Functional Programming: First-Class Functions, Higher-Order Functions Compiler-Bau: DSLs (Domain-Specific Languages)
Code-Metriken im Vergleich
Metrik | V0.1 (Imperativ) | V0.2 (Deklarativ) | Verbesserung |
Zeilen Code 285 | 80 (60 Regeln + Infrastruktur) | -72% |
Zyklomatische Komplexität 150 | 3-5 pro Regel | -95% |
Test-Coverage ~60% (schwierig) | 95% (isoliert) | +58% |
Änderungsaufwand 30 Min (Risiko hoch) | 5 Min (isoliert) | -83% |
Bugs pro 100 LOC ~4 | <1 | -75% |
Praktische Übungen Übung 1: Neue Regel hinzufügen Aufgabe: Füge eine Regel für :57 hinzu: "BALD ZWÖLF UHR" { range: [57, 57], handler: (ctx) => { const nextHour = ctx.hours[(ctx.h12 + 1) % 12]; if(ctx.hasBald) { return ["BALD", nextHour, "UHR"]; } return ["DREI", "VOR", nextHour]; } }
Übung 2: Refactoring V0.1 → V0.2 Aufgabe: Wandle diesen imperativen Code um: // Imperativ if(mm >= 5 && mm <= 9) { if(mm === 9 && hasFast) { words.push("FAST", "ZEHN", "NACH"); } else { words.push("FÜNF", "NACH"); } }
Lösung
// Deklarativ { range: [5, 9], handler: (ctx) => { if(ctx.mm === 9 && ctx.hasFast) { return ["FAST", "ZEHN", "NACH", ctx.hourWord]; } return ["FÜNF", "NACH", ctx.hourWord]; } }
Weiterführende Konzepte
Für fortgeschrittene Schüler
DSLs (Domain-Specific Languages)
-
CharGraph-Regeln als eigene "Sprache"
-
SQL, CSS, HTML als deklarative DSLs
Functional Reactive Programming
-
Daten-Streams und Transformationen
-
RxJS, React Hooks
Constraint Logic Programming
-
Prolog, MiniZinc
-
Automatische Lösungsfindung
Lernziel: Moderne Software-Paradigmen verstehen und anwenden
Constraint-Satisfaction