Das Besucher-Muster (Visitor Pattern) gehört zu den klassischen Entwurfsmustern der objektorientierten Programmierung und hat sich insbesondere in Java als ein beliebtes Mittel etabliert, um Algorithmen von Datenstrukturen zu trennen. Traditionell ermöglicht das Muster, neue Operationen über eine Objektstruktur auszuführen, ohne die Klassen der zu verarbeitenden Objekte zu verändern. Doch die Umsetzung war lange Zeit mit erheblichem Boilerplate-Code, vielen Schnittstellen und komplexen Vererbungsstrukturen verbunden. Die Einführung moderner Sprachfeatures in Java, insbesondere ab Version 14 bis hin zu Java 21, stellt nun die Weichen, das Besucher-Muster komplett neu zu denken. Die Schlüsselrolle spielt dabei Data Oriented Programming (DOP), ein Paradigma, das Datentypen und deren Mustererkennung in den Mittelpunkt stellt, und so elegantere und lesbarere Lösungen ermöglicht.
In diesem Kontext lässt sich das Besucher-Muster nicht nur eleganter implementieren, sondern bedingt durch Sprachkonstrukte wie versiegelte Schnittstellen (sealed interfaces), Records und eine erweiterte Pattern-Matching-Funktionalität sogar vollständig überflüssig machen. Als Beispiel dient in jüngster Zeit ein Buchkurationssystem, das klassisch mit dem Besucher-Muster umgesetzt wurde, nun aber mithilfe von Data Oriented Programming (DOP) neu modelliert und implementiert wurde. Dieses Beispiel illustriert die Stärken von Java 21 und gibt wertvolle Einblicke, wie sich komplexe Verarbeitungen ohne den traditionellen Besucher-Code gestalten lassen. Die klassische Implementierung des Besucher-Musters in Java war geprägt von einer Baumstruktur von abstrakten Klassen und konkreten Erben, die jeweils eine accept()-Methode implementieren, über die der Besucher via double dispatch auf den richtigen visit()-Methodenaufruf gelangt. Obwohl dieses Vorgehen die Erweiterbarkeit und Trennung von Logik und Daten verbessert, bringt es weitreichende Nachteile mit sich: Die vielen Klassen und Schnittstellen erzeugen umfangreichen und unübersichtlichen Code.
Die Vielzahl an Methodenaufrufen und expliziten Typprüfungen erschwert Wartung und Lesbarkeit, gerade bei komplexeren Regeln. Ein exemplarisches Szenario ist eine Bibliotheksanwendung, die Bücher unterschiedlichen Typs (Fiktion, Sachbücher, Kinderbücher) verarbeitet und für jeden Typ verschiedene Auswertungen liefert. Die Original-Visitor-Implementierung verarbeitete das genau und führte umfangreiche Typchecks und Verschachtelungen durch, welche insgesamt schwer nachvollziehbar waren. Im Gegensatz dazu stellt Data Oriented Programming die Struktur und Variationen der Daten in den Mittelpunkt und nutzt die Möglichkeiten moderner Sprachfeatures, diese eleganter abzubilden. Sealed Interfaces erlauben es, eine geschlossene Typ-Hierarchie zu modellieren, bei der der Compiler garantiert, dass alle Subtypen bekannt sind.
Dies schafft eine sichere Grundlage für Pattern Matching mittels switch-Ausdrücken, die jetzt so mächtig sind, dass sie das Auge kaum noch verwirren. Das Herzstück der neuen Herangehensweise sind Records, die als adäquate Repräsentation produktorientierter Datenmodelle dienen. Records bieten einen schlanken Weg, unveränderliche Datenobjekte zu deklarieren, und die erweiterte Pattern-Matching-Syntax erlaubt deren destrukturierte Betrachtung in match- und switch-Klauseln ohne komplizierte Getter oder explizite Castings. So liest sich das Beispiel mit den Sci-Fi-Büchern, die verschiedene Themen wie Raumfahrt oder Zeitreisen abdecken, sehr prägnant mit nur noch ein bis zwei Zeilen pro Fall, weil nur die für das aktuelle Szenario relevanten Felder entnommen werden. Ein besonders beeindruckender Fortschritt ist die Verwendung von sogenannten Guarded Patterns, die innerhalb eines switch-Branchs Bedingungen erlauben.
So lassen sich Fälle mit bestimmten Eigenschaften (etwa nur interessante Sachbücher, definiert durch einen interessanten Faktor und zwei spezifischen Bewertungen) filtern, ohne eine separate if-Struktur einzubauen. Diese Kombination macht den Code außerdem deutlich fehlerresistenter, da der Compiler eine lückenlose Behandlung aller möglichen Buchtypen erzwingt. Das Resultat ist eine hochgradig deklarative, leicht wartbare Lösung, die das Besucher-Muster nicht nur neu interpretiert, sondern letztlich überflüssig macht. Ein weiterer großer Vorteil ist die stark reduzierte Komplexität. Wo früher mehrere visitor- und visitable-Klassen erforderlich waren, besteht der gesamte Verarbeitungslogik-Code heute aus wenigen switch-Ausdrucksverzweigungen, die kompakt, klar strukturiert und viel weniger fehleranfällig sind.
Das führt nicht zuletzt zu schnellerem Verständnis neuer Entwickler im Team und erleichtert spätere Erweiterungen wie die Einfüge neuer Buchtypen oder zusätzlicher interessanter Regeln. Die enge Verzahnung von sealed interfaces, Records und Pattern Matching lässt sich als moderner Baukasten betrachten, welcher die viel beschworene Trennung von Daten und Verhalten sowie die klare Strukturierung von Algorithmen in Objektorientierung nicht nur unterstützt, sondern gleichsam neu definiert und optimiert. Für Java-Entwickler bringt das die spannende Möglichkeit, bewährte Entwurfsmuster hinter sich zu lassen und gleichzeitig alle Vorteile sowie die Robustheit moderner Softwarearchitekturen zu bewahren. Die Vorstellung, Besucher-Muster müsse man zwangsläufig mit viel Boilerplate und komplizierten Visitor-Hierarchien umsetzen, wird damit obsolet. Stattdessen heißt es heute ganz pragmatisch: „Nutze die Sprache.
“ Sehr spannend ist zudem der Ausblick in die Zukunft von Java, denn Preview-Features wie die Behandlung von primitiven Datentypen in Patterns ermöglichen bereits jetzt eine noch elegantfache Handhabung sehr spezifischer Fälle, die sonst große Umwege notwendig gemacht hätten. So lässt sich etwa die Verarbeitung von Kinderbüchern, deren Seitenzahl ein starkes Filterkriterium darstellt, prägnant und parallelisiert mit Zahlenwert-Vergleichen innerhalb eines switch realisieren. Dies verschlankt nicht nur den Code, sondern steigert auch dessen Performanz und Klarheit. Für alle, die bislang Stolz auf Ihr Know-how in klassischen Visitor-Pattern-Implementierungen waren, ergibt sich hier ein Wendepunkt: Weg von klassen- und methodenzentrierten Konstrukten hin zu deklarativen Datenmustern und Expressions. Das bedeutet auch, dass Java sich weiter entwickelt von einer strikt objektorientierten Programmiersprache hin zu einem multiparadigmatischen Werkzeug mit Fokus auf Ausdrucksstärke, Sicherheit und Entwicklerfreundlichkeit.
Diese Entwicklung ist kein Einzelfall, sondern Teil eines umfassenden Trends in der Softwareentwicklung, der mit Features wie algebraischen Datentypen und ausgefeiltem Pattern Matching aus statisch typisierten Sprachen wie Kotlin, Scala oder Rust (welche schon lange solche Konzepte nutzen) inspiriert ist. Abseits der reinen Sprachebene führt das verschlankte Visitor-Alternativparadigma auch zu Vereinfachungen beim Testen, Debuggen und Refaktorisieren von Software. Denn keine Vielzahl von kleineren Visitor-Implementierungen oder komplex verschachtelten Vererbungsstrukturen mehr, sondern klare, kompakte Switch-Ausdrucksblöcke, die direkt die Datenstruktur spiegeln und deren Ausprägungen explizit behandeln. Insgesamt zeigt sich damit, dass moderne Java-Versionen und DOP-Techniken nicht nur bestehende Entwurfsmuster überdenken, sondern auch das Potenzial besitzen, die Art und Weise zu transformieren, wie Programmierer Software strukturieren, warten und erweitern. Das Buchkurationsbeispiel ist dabei nur ein Vorgeschmack auf das, was Java 21 und danach möglich machen.
Für Entwickler bietet es die Chance, bewährte Muster in einem neuen Licht zu sehen und durch modernere Sprachmittel zu ersetzen. Letztlich liegt die Zukunft darin, die Ausdruckskraft der Sprache vollständig auszunutzen und so nicht nur eleganter, sondern auch nachhaltiger und sicherer zu programmieren. Ein Paradigmenwechsel, der das Besucher-Muster tatsächlich „besucht“ und neu erfindet – mit den Mitteln, die Java heute bietet.