Start / Seminare / Java SE Fortgeschrittene Sprachfeatures

Modul

Datenmodellierung

3 Kapitel in diesem Modul-Video · Laufzeit

Vertonung mit synthetischer Stimme · Inhalte redaktionell verantwortet · © 2026 HECKER CONSULTING, alle Rechte vorbehalten

Transkript

Der gesprochene Text dieses Moduls zum Mitlesen, Überfliegen und Durchsuchen. Ein Klick auf einen Zeitstempel springt an die Stelle im Video.

Datenmodellierung

0:00 In den ersten beiden Modulen haben wir zwei Werkzeuge kennengelernt, die zunächst wenig miteinander zu tun zu haben schienen: Records auf der einen Seite, versiegelte Typen auf der anderen. In diesem Modul kommen sie zusammen — und dabei zeigt sich, dass sie zwei Hälften derselben Sache sind. Es gibt eine verblüffend einfache Denkfigur dahinter, die aus der Mathematik stammt, für die Sie aber keine Mathematik brauchen. Sie besteht aus genau zwei Fragen.

0:25 Wenn Sie diese beiden Fragen an einen Fachbegriff stellen, fällt Ihnen das Modell fast von selbst in den Schoß. Genau darum geht es hier.

Datenmodellierung

0:34 Wir schließen den ersten Tag mit dem Modul ab, das die beiden vorherigen zusammenbindet. Zuerst schauen wir uns die Denkfigur an — Produkttypen und Summentypen, oder in Alltagssprache: UND und ODER. Dann übersetzen wir einen ganz gewöhnlichen Beleg aus dem Lager in ein Modell und sehen, wie sich die Struktur des Papiers in Typen abbildet.

0:53 Und zum Schluss kommt die Belohnung für die Sorgfalt: eine Fallunterscheidung, deren Vollständigkeit der Compiler garantiert. Das ist der Punkt, an dem sich der Aufwand der beiden ersten Module auszahlt. Der Aufbau folgt der Reihenfolge, in der Sie auch in einem echten Projekt vorgehen würden. Erst das Zerlegen: Welche Teile eines Begriffs gehören zusammen, welche schließen sich aus?

1:15 Dann die Übersetzung nach Java, und die ist mechanisch, sobald die erste Frage geklärt ist. Danach entsteht ein vollständiges Modell für eine Lieferung. Und am Ende steht der eigentliche Gewinn: eine Auswertung, bei der Ihnen der Compiler meldet, wenn Sie einen Fall vergessen haben. Behalten Sie diesen letzten Punkt im Hinterkopf — er ist der Grund, warum sich sauberes Modellieren rechnet.

Produkttypen und Summentypen

1:38 Fangen wir mit der Theorie an — aber keine Sorge, sie ist deutlich kürzer, als der Name vermuten lässt. Algebraische Datentypen klingen nach Vorlesung und nach Mathematik. In Wahrheit stecken dahinter zwei Fragen, die Sie an jeden Fachbegriff stellen können, ohne eine Formel zu bemühen. Und das Schöne daran: Die Antworten liefern Ihnen unmittelbar die passenden Java-Konstrukte.

2:00 Wenn Sie dieses Kapitel verlassen, haben Sie ein Werkzeug, das Sie in jedem Modellierungsgespräch einsetzen können — auch ohne Rechner. Es gibt genau zwei Bausteine. Ein Produkttyp bündelt mehrere Angaben zu einer Einheit — die Teile stehen in einer UND-Beziehung. Ein Summentyp beschreibt die Wahl zwischen Alternativen — die Möglichkeiten stehen in einer ODER-Beziehung.

2:23 Denken Sie an ein Formular: Name UND Anschrift UND Geburtsdatum, das ist ein Produkt. Und darunter ein Ankreuzfeld: Post ODER E-Mail ODER Telefon, das ist eine Summe. Mehr Bausteine braucht ein Datenmodell nicht. Die Namen stammen aus der Mengenlehre — die Zahl der möglichen Werte ist einmal das Produkt, einmal die Summe der Teilmengen.

2:44 Aber diese Herleitung dürfen Sie getrost vergessen. Und so übersetzt sich das nach Java. Für das UND nehmen Sie einen Record, das haben wir in Modul eins gesehen — die Komponentenliste ist genau die Aufzählung der Bestandteile. Für das ODER gibt es zwei Möglichkeiten, und die Wahl dazwischen ist einfacher, als sie aussieht.

3:04 Tragen die Varianten eigene Daten, brauchen Sie eine versiegelte Klasse oder Schnittstelle. Sind es dagegen reine Einzelwerte ohne weitere Angaben, genügt ein Enum. Diese Zuordnung ist der ganze Übersetzungsschritt. Die eigentliche Arbeit liegt davor, beim Beantworten der beiden Fragen. Warum lohnt sich dieser Umweg über die Begriffe? Weil Sie die Frage stellen können, bevor Sie eine Zeile Code schreiben — und zwar an die Fachabteilung, in ganz normaler Sprache.

3:32 Ein Artikel ist Nummer UND Bezeichnung UND Bestand, darüber wird niemand streiten. Eine Buchung ist Zugang ODER Abgang ODER Korrektur, auch das ist unstrittig. Sie haben damit ein Modell, bevor Sie über Klassen nachdenken. Und der praktische Zusatznutzen ist erheblich: Wer beides sauber trennt, bekommt vom Compiler die Vollständigkeitsprüfung geschenkt.

3:52 Wer es vermischt, bezahlt später mit Sonderfällen im Code. Hier stehen beide Bausteine direkt untereinander, und das Bemerkenswerte ist die Länge. Der Produkttyp ist eine Deklaration, der Summentyp ebenfalls. Beides zusammen sind vier Zeilen. Das ist der Punkt, den ich Ihnen mitgeben möchte: Der Aufwand beim Modellieren liegt nicht im Tippen, sondern im Nachdenken davor.

4:15 Wenn Sie wissen, was UND und was ODER ist, schreibt sich der Code in einer Minute. Wenn Sie es nicht wissen, helfen Ihnen auch dreihundert Zeilen nicht weiter. Achten Sie auf die Kommentare — sie benennen genau die Beziehung, um die es geht. Diese Aufgabe hat eine ungewöhnliche Anweisung: Bleiben Sie zunächst weg von der IDE.

4:35 Nehmen Sie sich einen Begriff aus dem Lagerpuls-Umfeld und zerlegen Sie ihn auf Papier — jeden Bestandteil als UND oder als ODER markiert. Der Grund für die Einschränkung ist ganz praktisch. Sobald der Editor offen ist, modellieren Sie Klassen statt Fachlichkeit, und dann bestimmt die Technik die Struktur. Der Erfolg zeigt sich daran, dass Ihre Skizze ohne Java-Begriffe auskommt. Wenn darin schon Klassen und Felder vorkommen, sind Sie einen Schritt zu weit.

5:02 Der erste Fehler ist der teuerste und leider auch der verbreitetste: Varianten als Flag-Felder in ein und demselben Record abzubilden. Dann gibt es plötzlich Kombinationen, die fachlich unmöglich sind, aber technisch existieren — und irgendwo im Code steht dafür eine Prüfung, die niemand mehr versteht. Der zweite Punkt: Ein offener Summentyp verliert die Vollständigkeitsprüfung; ohne sealed ist der halbe Nutzen weg.

5:25 Der dritte betrifft die Wahl zwischen Enum und versiegeltem Typ — tragen die Varianten eigene Daten, ist das Enum die falsche Wahl. Und der vierte: Ein Modell, das an der Datenbanktabelle entlanggebaut ist, spricht nicht die Sprache der Fachlichkeit.

Eine Lieferung modellieren

5:40 Genug Theorie. Nehmen wir einen Beleg, wie er täglich im Lager über den Tisch geht, und übersetzen ihn Schritt für Schritt in ein Modell. Ich habe bewusst ein Papierdokument gewählt, denn das ist der Normalfall in Projekten: Am Anfang steht ein Formular, ein Ausdruck oder eine Bildschirmmaske, die jemand aus der Fachabteilung mitbringt.

5:58 Sie werden sehen, dass dieser Beleg seine Struktur selbst verrät — man muss ihn nur mit den beiden Fragen aus dem ersten Kapitel im Kopf lesen. Unsere Lieferung besteht aus einer Nummer, einem Lieferanten und einer Liste von Positionen — das ist ein klares UND, also ein Produkttyp. Interessanter sind die Positionen. Auf dem Papier stehen dort Zeilen, die unterschiedlich aussehen: Mal ist es ein Artikel mit Menge und Einzelpreis, mal ein Frachtzuschlag mit Grund und Betrag.

6:27 Diese Zeilenarten schließen sich gegenseitig aus — eine Position ist das eine oder das andere, nie beides. Damit ist die Position ein Summentyp. Und jede einzelne Variante ist wiederum ein Produkttyp. So verschachteln sich die beiden Bausteine in der Praxis. Das Vorgehen dahinter ist immer dasselbe, und Sie können es auf jeden Beleg anwenden. Sie gehen ihn durch und notieren jedes Feld. Dann bündeln Sie, was zusammengehört.

6:52 Dann sammeln Sie die Zeilenarten, die sich ausschließen, und fassen sie unter einem Oberbegriff zusammen. Und dann schreiben Sie jede Variante mit genau ihren eigenen Feldern. Der letzte Schritt ist zugleich Ihr Prüfstein: Wenn eine Variante ein Feld braucht, das nur manchmal gefüllt ist, dann stimmt der Schnitt noch nicht.

7:11 Dann verstecken sich in dieser Variante in Wahrheit zwei. Die Lieferung selbst ist unspektakulär — ein Record mit drei Komponenten. Eine Zeile darin verdient trotzdem Aufmerksamkeit: die defensive Kopie im kompakten Konstruktor. Das ist genau der Mechanismus aus Modul eins, und er gehört hierher, weil ein Datenmodell nur dann etwas wert ist, wenn es seine Zusagen auch hält.

7:34 Eine Lieferung, deren Positionsliste sich nachträglich von außen verändern lässt, ist kein verlässliches Modell, sondern ein Formular mit Bleistift. Solche Details entscheiden darüber, ob man einem Modell trauen kann. Und hier die andere Hälfte. Oben die versiegelte Schnittstelle mit ihren zwei erlaubten Varianten, darunter die Varianten selbst als Records.

7:55 Worauf es ankommt, steht in der Fußzeile: Jede Variante trägt genau ihre eigenen Felder. Der Frachtzuschlag hat keine Artikelnummer — nicht, weil sie leer bliebe, sondern weil es sie schlicht nicht gibt. Vergleichen Sie das mit dem, was man sonst häufig sieht: eine einzige Klasse mit allen Feldern und der Hälfte davon auf null.

8:14 Solche Modelle zwingen jeden Leser, sich die Regeln selbst zusammenzureimen. Der Nutzen wird am deutlichsten, wenn man ihn negativ formuliert: Was kann jetzt nicht mehr passieren? Eine Artikelposition ohne Menge lässt sich gar nicht erst erzeugen. Ein Frachtzuschlag kann keine Artikelnummer tragen. Und wenn irgendwann eine dritte Positionsart dazukommt, erfahren Sie das beim Übersetzen, nicht im Betrieb.

8:38 Das ist der eigentliche Ertrag guter Modellierung — sie verlagert Fehler aus der Laufzeit in den Compiler. Jede Prüfung, die Sie nicht schreiben müssen, ist eine Prüfung, die auch nicht vergessen werden kann. Jetzt setzen Sie Ihre Skizze in Code um. Das ist der mechanische Teil und sollte zügig gehen — wenn nicht, war die Skizze noch nicht fertig.

8:59 Spannend wird die Stretch-Variante: Ergänzen Sie eine dritte Variante für Pfandrückgaben und beobachten Sie, wo der Compiler danach Alarm schlägt. Diese Beobachtung nehmen wir gleich im dritten Kapitel wieder auf, denn sie ist der Kern der ganzen Übung. Ein gutes Modell meldet sich, wenn es unvollständig behandelt wird — und zwar an jeder betroffenen Stelle.

9:20 Der erste Punkt ist ein Reflex aus der objektorientierten Welt: gemeinsame Felder nach oben ziehen. Bei Schnittstellen geht das gar nicht, und das ist kein Mangel — der Oberbegriff soll ja nur die Menge der Varianten benennen. Der zweite Punkt ist ein Klassiker aus dem Finanzbereich: BigDecimal gegen double tauschen und sich Monate später über Rundungsdifferenzen wundern.

9:40 Der dritte ist die vergessene Kopie, die wir eben besprochen haben. Und der vierte ist der subtilste: Varianten nach technischen Kriterien zu schneiden statt nach fachlichen. Dann passt das Modell zur aktuellen Implementierung, aber nicht zur Fachlichkeit — und die überlebt die Implementierung.

Pattern Matching über das Modell

9:57 Und jetzt kommt die Belohnung für die Sorgfalt der letzten beiden Kapitel. Wir haben ein Modell, in dem der Compiler alle Varianten kennt — er weiß, dass es genau zwei Positionsarten gibt und keine dritte. Dieses Wissen lässt sich ausnutzen, und zwar so, dass eine ganze Fehlerklasse aus Ihrem Projekt verschwindet: der vergessene Fall.

10:16 Wir schauen uns an, wie das aussieht, warum ein bestimmter Zweig dabei fehlen muss, und welche Entwurfsentscheidung dahinter steckt. Ein switch-Ausdruck über einen versiegelten Typ wird vom Compiler auf Vollständigkeit geprüft. Er kennt die erlaubten Varianten und verlangt für jede einen Fall. Dazu kommt ein zweites Sprachmittel: Record-Muster zerlegen die Variante gleich beim Treffen in ihre Komponenten, sodass Sie nicht erst umwandeln und dann Zugriffsmethoden aufrufen müssen.

10:43 Und für Werte, die Sie in diesem Fall gar nicht brauchen, gibt es den Unterstrich. Zusammen ergibt das Code, der ziemlich genau so aussieht wie das, was man auf einem Blatt Papier notieren würde. Sehen wir uns die Berechnung an — konzeptionell, nicht Zeile für Zeile. Der Ablauf ist: über die Positionen laufen, jede Position auf ihren Beitrag abbilden, alles aufsummieren.

11:05 Der interessante Teil steht in der Mitte, im switch. Dort gibt es für jede Variante genau einen Fall, und jeder Fall greift sich per Record-Muster nur die Werte heraus, die er braucht — die Unterstriche markieren, was ignoriert wird. Und dann achten Sie bitte auf das, was nicht da ist: Es gibt keinen default-Zweig. Genau darum geht es auf der nächsten Folie.

11:26 Der fehlende default ist kein Versehen, sondern der Kern der Sache. Der Compiler kennt alle Varianten und meldet jede, die Sie nicht behandelt haben. Sobald Sie einen default ergänzen, ist diese Prüfung abgeschaltet — eine neue Variante landet dann stillschweigend im Sammelzweig, und niemand erfährt davon. Das ist der Unterschied zwischen einem Fehler, der Sie beim Übersetzen anspringt, und einem, der im Betrieb ein falsches Ergebnis liefert.

11:51 Nur bei offenen Hierarchien ist ein default sinnvoll, weil dort die Garantie fehlt. Bei versiegelten Typen ist er ein Rückschritt. Zum Abschluss des Kapitels noch eine Einordnung, die über die Syntax hinausgeht. Der Stil, den wir hier gewählt haben, trennt Daten und Verhalten: Die Records tragen Werte, die Berechnung steht daneben.

12:11 Klassisch objektorientiert würde man es andersherum machen und jeder Variante eine eigene Methode geben. Beides ist tragfähig, und es gibt kein richtig oder falsch. Es gibt nur eine Frage, die man sich stellen sollte — und die beantwortet die nächste Folie. Die Frage lautet: Was wächst in Ihrem Projekt häufiger — die Auswertungen oder die Varianten?

12:32 Wenn ständig neue Auswertungen dazukommen und die Varianten stabil bleiben, ist die Trennung im Vorteil: Sie schreiben einen neuen switch und fassen die Records nicht an. Wenn dagegen laufend neue Varianten entstehen und die Auswertungen feststehen, gewinnt der objektorientierte Weg — eine neue Klasse genügt, und der Compiler erinnert Sie an die Methode.

12:52 Diese Frage ist älter als Java und hat einen Namen: das Expression Problem. Eine allgemeingültige Antwort gibt es nicht, aber eine passende für Ihren Fall. Hier üben Sie das Wesentliche: eine Fallunterscheidung so zu schreiben, dass der Compiler für die Vollständigkeit geradesteht. Der Erfolgsmoment kommt beim zweiten Teil — Sie fügen eine dritte Variante hinzu, und der Compiler nennt Ihnen genau die Fundstelle, an der etwas fehlt.

13:17 Schreiben Sie danach noch eine zweite Auswertung, etwa die Zahl der Artikelpositionen, und vergleichen Sie den Aufwand. Das ist die praktische Erfahrung, die Ihnen bei der Stilfrage von eben später weiterhilft. Der erste Punkt ist tückisch, weil die Werkzeuge dagegenarbeiten: Manche IDEs schlagen einen default-Zweig vor, und der Vorschlag sieht hilfreich aus. Er ist es nicht.

13:39 Der zweite Punkt betrifft die Lesbarkeit — Record-Muster mit vollständiger Komponentenliste zu schreiben, wo Unterstriche genügen, macht den Code unnötig laut. Der dritte ist subtil und wichtig: Nur der switch als Ausdruck wird auf Vollständigkeit geprüft, als reine Anweisung entfällt die Prüfung. Und der vierte ist eine Frage der Konsequenz — die Berechnung nachträglich doch in die Records zu ziehen, ergibt einen halben Stilwechsel und damit das Schlechteste aus beiden Welten.

14:06 Der erste Tag endet mit einer Werkzeugkiste, die vollständig ist. Jedes Fachkonzept zerfällt in UND-Bestandteile und ODER-Varianten, und für beides gibt es in Java ein passendes Mittel. Jede Variante trägt genau ihre eigenen Felder — keine leeren Platzhalter, keine unmöglichen Kombinationen. Und ohne default übernimmt der Compiler die Vollständigkeitsprüfung.

14:27 Morgen wechseln wir die Perspektive: Wir schauen uns Enums genauer an — den anderen Summentyp, den viele für eine schlichte Namensliste halten und der in Wahrheit deutlich mehr kann.

Lieber mit Trainer? Dieses Modul ist Teil unserer Team-Schulungen — mit Übungen, Ihrem eigenen Code und Fragen, die ein Video nicht beantwortet. Mehr erfahren →