Kapitel 8 von 18

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

Fortschritt44%

Kommt als nächstes:

Constraint-Satisfaction