Deklarative Programmierung in V0.2

Paradigmenwechsel: Von Imperativ zu Deklarativ

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-4: NACH und FÜNF NACH
  {
    range: [1, 4],
    handler: (ctx) => ["NACH", ctx.hourWord]
  },

  // Regel 5-9: FÜNF NACH
  {
    range: [5, 9],
    handler: (ctx) => ["FÜNF", "NACH", ctx.hourWord]
  },

  // Regel 10-14: ZEHN NACH
  {
    range: [10, 14],
    handler: (ctx) => ["ZEHN", "NACH", ctx.hourWord]
  },

  // Regel 15-19: VIERTEL NACH
  {
    range: [15, 19],
    handler: (ctx) => ["VIERTEL", "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)];
}

Hinweis: Die obigen Regeln sind vereinfacht dargestellt. Die echte Implementierung berücksichtigt auch optionale Modifikatoren wie FAST, KURZ und BALD mit komplexen Gültigkeits- und Prioritätsregeln. Für die vollständige Spezifikation siehe Kapitel 19: Fallback-Logik und Prioritätsmodifikatoren.

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 :55 hinzu: "FÜNF VOR [STUNDE]"

{
  range: [55, 59],
  handler: (ctx) => {
    const nextHour = ctx.hours[(ctx.h12 + 1) % 12];
    return ["FÜNF", "VOR", nextHour];
  }
}

Übung 2: Refactoring V0.1 → V0.2 Aufgabe: Wandle diesen imperativen Code um:

// Imperativ
if(mm >= 5 && mm <= 14) {
  if(mm >= 5 && mm <= 9) {
    words.push("FÜNF", "NACH");
  } else if(mm >= 10 && mm <= 14) {
    words.push("ZEHN", "NACH");
  }
}

Lösung

// Deklarativ
{
  range: [5, 9],
  handler: (ctx) => ["FÜNF", "NACH", ctx.hourWord]
},
{
  range: [10, 14],
  handler: (ctx) => ["ZEHN", "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