Design patterns in Java

Die 10 beliebtesten Entwurfsmuster in Java

/ 11.01.2024 Java

Bei der Softwareentwicklung ist es von entscheidender Bedeutung, den Entwurf so flexibel zu gestalten, dass er leicht zu warten und effizient ist und mit sich ändernden technologischen oder geschäftlichen Bedingungen arbeiten kann. Damit unser Anwendungscode diese Bedingungen erfüllen kann, müssen wir bewährte Praktiken anwenden, zu denen auch die Wahl der richtigen Entwurfsmuster gehören.  

Java ist eine der populärsten Programmiersprachen und hat eine ganze Reihe von Software-Entwurfsmustern, die jeder Java-Programmierer kennen sollte. In diesem Artikel werden wir die beliebtesten Muster kennenlernen und analysieren, wann und für was sie sinnvoll verwendet werden können. 

Was sind Software-Entwurfsmuster?  

Ein Entwurfsmuster ist eine wiederverwendbare Lösung für ein typisches Problem, das beim Softwareentwurf auftritt und auf verschiedene Situationen angewendet werden kann. Es handelt sich dabei nicht um fertige Entwürfe oder Code, sondern um bewährte Praktiken vieler Softwareentwickler mit gebrauchsfertigen, wiederverwendbaren Lösungen für ausgewählte Probleme, die beim Entwurf objektorientierter Lösungen auftreten

Eine Schlüsselrolle im Prozess der Popularisierung des Konzepts der Entwurfsmuster spielten die Autoren Erich Gamma, Richard Helm, Ralph Johnson und John Vlissides, die den Bestsellers „Design Patterns: Elements of Reusable Object-Oriented Software” schrieben, zusammen bekannt als die „Gang of Four” (GoF).  

Seit 1994 wurden Dutzende weitere Java-Entwurfsmuster für die objektorientierte Programmierung entdeckt. Der „Pattern-Ansatz” ist in vielen Frameworks sehr populär geworden. Infolgedessen gibt es jetzt viel mehr Muster außerhalb des objektorientierten Designs. Softwareentwickler verwenden diese Muster ständig, manchmal sogar unbewusst. Andererseits ist es sinnvoll, sie absichtlich einzusetzen, um mit Programmierern zu sprechen, die dieselben Sprachelemente verwenden und denselben Quellcode teilen.  

Warum sind Java-Entwurfsmuster wichtig?   

Java-Entwurfsmuster haben aus mehreren Gründen an Popularität gewonnen. Erstens fördern sie wiederverwendbare objektorientierte Software und ermöglichen es Entwicklern, modularen und erweiterbaren Code zu schreiben. Effiziente Java-Programmierung mit Entwurfsmustern, führt dazu dass sich Softwaresysteme langfristig leichter warten und aktualisieren lassen.  

Das Factory-Muster beispielsweise ist ein Software-Entwurfsmuster, das eine Schnittstelle für die Erstellung von Objekten bereitstellt, es aber Unterklassen erlaubt, zu entscheiden, von welcher Klasse eine Instanz erstellt werden soll. Dies fördert die Wiederverwendung von Code, da die Entwickler Objekte erstellen können, ohne die genaue Klasse des zu erstellenden Objekts angeben zu müssen.   

Zweitens verbessern Entwurfsmuster die Lesbarkeit und Wartbarkeit des Codes. Indem sie einer Standardstruktur folgen, machen Muster den vorhandenen Code leichter verständlich, was besonders hilfreich ist, wenn man in Teams arbeitet oder nach langer Zeit zur Codebasis zurückkehrt.  

Drei häufig verwendete Kategorien von Entwurfsmustern 

Die universellsten und anspruchsvollsten Muster, mit denen wir uns heute befassen werden, sind die Architekturmuster. Entwickler können sie auf praktisch jede Programmiersprache anwenden, und sie verwenden sie, um die Architektur einer gesamten Anwendung zu entwerfen. 

Nach dem bereits erwähnten Handbuch lassen sich 23 Entwurfsmuster je nach Aufgabenstellung in drei Kategorien einteilen: Kreative, strukturelle und verhaltensbezogene Muster. Werfen wir nun einen genaueren Blick auf einige der wichtigsten verwendeten Java-Entwurfsmuster.

Kreative Java-Entwurfsmuster 

Diese Entwurfsmuster befassen sich mit der Erstellung von Klassen, Methoden und Datentypen, wobei der Schwerpunkt auf dem Prozess der Objekterstellung liegt und versucht wird, Objekte so zusammenzustellen, dass sie der jeweiligen Situation angemessen sind. Die Entwurfsmuster zielen darauf ab, die Objekterstellung flexibler zu gestalten und vom Clientcode zu trennen, um die Wiederverwendbarkeit und Wartbarkeit des Codes zu fördern.    

  • Factory 

Das Factory-Muster bietet eine Schnittstelle für die Erstellung von Objekten in einer Oberklasse, erlaubt es aber Unterklassen, die Typen der erstellten Objekte zu ändern. Es wird verwendet, wenn ein System bei der Erstellung, Komposition und Darstellung von Objekten unabhängig sein muss. Dadurch kann eine Klasse die Verantwortung für die Erstellung von Instanzen an ihre Unterklassen delegieren.   

Dieses Erstellungsmuster wird häufig in Szenarien verwendet, in denen es mehrere Klassen gibt, die eine gemeinsame Schnittstelle implementieren oder eine gemeinsame Basisklasse erweitern. Es gibt verschiedene Varianten des Klassenerstellungsmusters, wie Simple Factory, Factory Method und Abstract Factory. Jede Variante hat ihre Vorteile und Anwendungsfälle, so dass Sie die für Ihre spezifischen Anforderungen am besten geeignete Variante auswählen können. 

  • Builder 

Das Builder-Muster ist ein kreatives Entwurfsmuster, das die Konstruktion eines komplexen Objekts von seiner Darstellung trennt, so dass derselbe Konstruktionsprozess verschiedene Darstellungen erzeugen kann. Es umfasst eine Director-Klasse, die den Konstruktionsprozess koordiniert, und eine Builder-Schnittstelle mit spezifischen Klassen, die die Konstruktionsschritte implementieren. 

Builder wird häufig von Entwicklern verwendet, die die Lombok library einsetzen, die Java-Code für uns generiert und unter anderem auch die Erstellung von Methoden ermöglicht, die für Builder verwendet werden. Das heißt, dass wir durch die Erstellung von Klassen und die Erzeugung neuer Objekte mühelos damit arbeiten können, ohne viel Code zu schreiben. Es wird verwendet, wenn ein Objekt mit vielen möglichen Konfigurationen konstruiert werden muss oder wenn der Konstruktionsprozess viele Schritte umfasst. 

  • Singleton 

Das Singleton-Entwurfsmuster stellt sicher, dass eine Klasse nur eine Instanz hat, die auch der globale Zugriffspunkt auf diese Instanz ist. Es ist äußerst nützlich, wenn es um Dienstimplementierungen geht, die wir im gesamten Programm nur einmal verwenden, unabhängig davon, wo sie erstellt werden. Das Singleton-Pattern ist eine Lösung, die vor allem für Verbindungen zur Datenbank oder zu Geräten, die über eine serielle Schnittstelle kommunizieren, verwendet wird. Indem Sie nur eine Instanz der Klasse haben, können Sie eine effiziente Nutzung der Ressourcen sicherstellen und unnötige Belastung vermeiden. 

  • Prototyp 

 Das Prototypmuster wird in der Regel verwendet, wenn wir eine Instanz einer Klasse (Prototyp) haben und neue Objekte durch einfaches Kopieren des Prototyps erstellen wollen. Dieses Muster ist besonders nützlich, wenn die Kosten für die Erstellung eines neuen Objekts teurer oder komplizierter sind als das Kopieren eines vorhandenen Objekts.  Es ist besonders nützlich in Szenarien, in denen die Objekterstellung dynamisch und flexibel sein muss. 

Strukturelle Java-Entwurfsmuster 

Strukturelle Entwurfsmuster in Java befassen sich mit den Beziehungen zwischen verwandten Objekten, die größere Strukturen bilden. Diese Muster helfen bei der Definition von Beziehungen zwischen Einheiten und vereinfachen den Entwurf komplexer Systeme. 

  • Decorator 

Das Decorator-Muster ermöglicht es Ihnen, einem einzelnen Objekt statisch oder dynamisch Verhalten hinzuzufügen, ohne das Verhalten anderer Objekte derselben Klasse zu beeinflussen. Es handelt sich um ein Strukturmuster, das eine Reihe von Decoratorklassen umfasst, die zum Umhüllen bestimmter Komponenten verwendet werden. Es wird am häufigsten verwendet, wenn das Verhalten einzelner Objekte erweitert werden soll, ohne deren Code zu verändern. 

  • Facade 

Das Facade-Muster bietet eine vereinfachte (aber begrenzte) Schnittstelle zu einem komplexen System von Klassen, einer Bibliothek oder einem Framework, reduziert die Gesamtkomplexität der Anwendung und hilft, unerwünschte Abhängigkeiten an einen einzigen Ort zu verlagern. Der Hauptvorteil des Facade-Musters besteht darin, die Schnittstellen zu vereinfachen und zu standardisieren, so dass das Teilsystem für den Kunden leichter zugänglich und einfacher zu verwenden ist. Facade-Muster werden am häufigsten in Anwendungen verwendet, die in Java geschrieben wurden, wenn sie mit komplexen Bibliotheken und APIs arbeiten. 

  • Proxy 

Ein Proxy-Entwurfsmuster ist ein Vertreter oder Proxy eines anderen Objekts, um einen überwachten Zugriff auf das Objekt zu erhalten, das es repräsentiert. Mit diesem Muster können wir die Last minimieren und die Leistung erhöhen.  Wenn wir eine Bibliothek verwenden müssen, von der wir wissen, dass sie die CPU des Servers stark belastet, können wir den Vorgang sozusagen auf den letzten Moment verschieben, in welchemsie verwenden werden muss. Bei Anwendungen sparen wir Speicher- und Hardwareressourcen für rechenintensive Operationen und laden komplexe Elemente nur, wenn sie benötigt werden. 

Verhaltensbezogene Java-Entwurfsmuster 

Sie betreffen das Verhalten kooperierender verwandter oder abhängiger Objekte und wie Klassen und Objekte interagieren, kommunizieren und kooperieren. Verhaltensmuster befassen sich in erster Linie mit der Delegation von Verantwortung zwischen bestehenden Objekten und den Mustern der Kommunikation zwischen ihnen. Sie werden sehr häufig in Unternehmensprojekten eingesetzt. 

Der Grund dafür ist, dass wir bei der Verwendung von Drittanbieter-Bibliotheken möglicherweise mit Speicherlecks zu kämpfen haben, die uns Probleme bereiten können, von denen wir erst in einigen Wochen oder Monaten erfahren werden. In solchen Situationen ist es sinnvoll, verschiedene Entwurfsmuster zu verwenden, um z. B. die Leistung der Anwendung zu überwachen. 

  • Strategy 

Ein Strategy-Entwurfsmuster definiert eine Familie von Algorithmen, kapselt jeden von ihnen und erlaubt ihre Änderung. Mit dem Strategy-Muster kann sich ein Algorithmus unabhängig von den Clients, die ihn verwenden, ändern. Es ermöglicht dem Client, zum Zeitpunkt der Ausführung aus der Familie der Algorithmen zu wählen. Der Grundgedanke des Strategy-Musters besteht darin, die Algorithmen von den Clients, die sie verwenden, zu trennen und so Flexibilität und Erweiterbarkeit zu fördern. 

  • Visitor 

Das Visitor-Muster ist für die Durchführung einer bestimmten Operation an einer komplexen Datenstruktur zuständig. Es ermöglicht Ihnen, eine neue Operation zu definieren, ohne die Klassen der Elemente zu ändern, auf denen sie ausgeführt wird. Das heißt, dass es zum Beispiel die Zeiten der Operation protokolliert oder uns sagt, wer, wann und wie einige Funktionen ausgeführt wurden.  

  • Observer 

 Das Observer-Muster definiert eine eins-zu-viele-Beziehung zwischen Objekten, so dass bei einer Zustandsänderung eines Objekts alle abhängigen Objekte automatisch benachrichtigt und aktualisiert werden. Einerseits sorgt das Beobachtermuster dafür, dass wir ein Modul haben, das uns Ereignisse meldet, z. B. dass ein Benutzer bestimmte Daten angefordert hat, und wir daraufhin ein Ereignis erzeugen, das wiederum eine Reaktion im anderen Teil des Systems auslöst und die entsprechenden Daten erzeugt. 

Drei Beispiele für die Verwendung von Entwurfsmustern in der Softwareentwicklung 

Hier finden Sie Codebeispiele für die praktische Anwendung von Java-Entwurfsmustern im Softwareentwicklungsprozess. 

Beispiel 1 

Im ersten Fall werden wir vier Arten von Software-Entwurfsmustern verwenden, um das Problem der Erstellung verschiedener Fahrzeugobjekte zu lösen, die bestimmte Verhaltensweisen aufweisen. 

Das mit der @SuperBuilder-Annotation erhaltene Builder-Entwurfsmuster wird zur Erstellung von Builder-Methoden während der Vererbung verwendet. Der reguläre @Builder von Lombok findet hier keine Anwendung. 

  • Factory-Entwurfsmuster, das in der Klasse VehicleFactory verwendet wird, um Objekttypen in Abhängigkeit vom Namen zu erzeugen. 
  • Die mit den VehicleCreator-Klassen definierten Factory Methods (Herstellungsverfahren) enthalten Einzelheiten zur Herstellung einzelner Fahrzeuge. 
  • Das in Form von EconomicDriving und AggressiveDriving verwendete Strategy-Muster definiert die Fahrstile der einzelnen Fahrzeuge. 

Ein Beispiel für die Verwendung von 4 Mustern zur Lösung des Problems der Erstellung verschiedener Fahrzeugobjekte, die ein bestimmtes Verhalten aufweisen.

Nach dem Start des Programms erhalten Sie diesen Bildschirm: 

  • 24.0L V12 diesel truck economic driving 
  • 6.0L V6 diesel bus economic driving 
  • 2.0L R4 petrol car aggressive driving 

Beispiel #2 

Im zweiten Beispiel werden wir das Memento als Verhaltensbezogenes Entwurfsmuster (es ermöglicht das Speichern und Wiederherstellen des vorherigen Zustands eines Objekts, ohne die Details seiner Implementierung preiszugeben) in einem Programm wie Paint verwenden, um die Möglichkeit einer 10-stufigen ‘Undo’- oder Rückwärtsfunktion zu erhalten. 

Wir können Formen auf der Leinwand (Canvas) zeichnen und sie mit Farben füllen. Jedes Mal, wenn wir auf Speichern drücken, erstellt und speichert Caretaker den aktuellen Zustand der Leinwand als CanvasMemento-Objekt. Wenn wir ‘Undo’ drücken, kehren wir zum vorherigen Zustand zurück. 

Nachdem Sie das Programm zweimal ausgeführt haben, speichern Sie die Änderungen, ändern Sie sie noch einmal und verwenden Sie dann zweimal Rückgängig, um die vorherigen Werte wiederherzustellen. 

Memento design pattern as example of architectural pattern 

Memento pattern

Auf dem Bildschirm sehen wir: 

  • Canvas after all operations: draw:Circle fill:Red draw:Triangle 
  • Canvas after undo draw triangle: draw:Circle fill:Red 
  • Canvas after undo fill red: draw:Circle 

Beispiel #3 

Das dritte Beispiel ist die Anwendung des Visitor-Entwurfsmusters auf einen BuildingInspector der die Aufgabe hat, bei der Besichtigung der einzelnen Räume eines Hauses Aktionen auszuführen, die nur ihm selbst bekannt sind. 

Sobald wir den Inspektor akzeptieren und in das Haus lassen (house.accept(inspector)), beginnt der BuildingInspector mit der Inspektion der in der Bauphase des Hauses definierten Räume. Er führt für jeden Raum spezifische Aktionen durch und prüft schließlich die Struktur des gesamten Hauses, wenn wir in der accept-Methode der House-Klasse “visitor.visit(this)” aufrufen. 

Visitor pattern separates algorithms from the objects on which they operate-better object composition 

Visitor pattern

Auf dem Bildschirm nach der Fertigstellung des Programms entsprechend der Reihenfolge der Räume bei der Erstellung des Hauses sieht es dann so aus: 

  1. Inspecting the quality of kitchen appliances and safety. 
  2. Checking living room space and ventilation. 
  3. Inspecting plumbing and hygiene conditions in the bathroom. 
  4. Performing an overall structural integrity check of the house. 

Was ist bei der Auswahl von Entwurfsmustern zu beachten? 

Bei der Implementierung von Entwurfsmustern ist es wichtig, das Projekt nicht zu sehr zu verkomplizieren. Es ist besser, mit einfachen Lösungen zu beginnen, während die Entwurfsmuster eingeführt werden, um das Problem der Komplexität zu lösen.   

Ein weiterer wichtiger Punkt ist die Einhaltung von Kodierungsstandards und bewährten Verfahren. Einheitliche Kodierungsstile machen es dem Team leichter, den Code zu verstehen und zu pflegen, auch bei der Verwendung von Entwurfsmustern.   

Um Teammitgliedern und anderen zukünftigen Code-Reviewern eine Orientierungshilfe zu geben, ist es notwendig, Designentscheidungen klar zu dokumentieren. Obwohl Entwurfsmuster die Wartung erleichtern können, kann eine unsachgemäße Implementierung zu Fehlern führen. Daher sollten immer umfassende Tests durchgeführt werden, um die korrekte Implementierung unserer Entwurfsmuster sicherzustellen. 

Entwurfsmuster beim Refactoring von Code – wie kann man sie sinnvoll einsetzen? 

Wenn das gewählte Entwurfsmuster nicht den erwarteten Nutzen bringt oder wenn sich die Anforderungen ändern, müssen Sie bereit sein, den Code zu überarbeiten, denn Entwurfsmuster sollten sich an die sich ändernden Anforderungen des Projekts anpassen.   

Beim Refactoring ist es sicherlich sinnvoll, das gesamte Software-Engineering-System ganzheitlich zu verstehen, damit wir bewusst entscheiden können, ob wir das System neu schreiben wollen und in welchem Umfang. D.h. es gilt dabei, zu beachten: Gibt es Leistungsprobleme? Liegt das Problem einfach darin, dass wir z. B. eine neue Version einer Bibliothek verwenden möchten, die eine völlig andere Schnittstelle hat?    

Wenn z. B. die Entscheidung getroffen wird, dass wir das Modul unter Berücksichtigung der aktuellen Struktur neu schreiben, und die Muster mit Bedacht eingesetzt werden, lohnt es sich, sie zu verwenden. Sie bieten die Möglichkeit, das Projekt ohne allzu großen Zeitaufwand zu erweitern und auszubauen. 

Andererseits kann die Einführung von Entwurfsmustern in ein Projekt, das willkürlich und unverständlich geschrieben wurde, dazu führen, dass wir im Grunde ein neues Projekt von Grund auf neu beginnen müssen. Wir verbringen zunächst einige Monate damit, eine ältere Version des Projekts zu verstehen, und stellen dann fest, dass manchmal 3 oder 4 Softwareentwurfstechniken wie Entwurfsmuster die zugrunde liegenden Probleme lösen könnten. Das bedeutet, dass wir von Anfang an, wenn wir die erste Stufe des Projekts, das MVP mit den wichtigsten Funktionen für den Kunden, erstellen, mehrere Muster verwenden und die nächsten Module nach diesen Mustern implementieren. 

Zusammenfassung    

In diesem Artikel haben wir nur einige der populären Java-Entwurfsmuster besprochen, wie z. B. kreative, strukturelle und verhaltensbasierte Muster, und erklärt, wie sie auf die Java-Programmierung angewendet werden können, aber es gibt noch viele andere Muster. Je besser Sie diese Software-Entwurfsmuster verstehen und bei Ihrer täglichen Arbeit anwenden, desto besser können Sie die Qualität Ihres Java-Codes verbessern, ihn leichter warten und ein erfahrener und effizienter Programmierer werden. 

Wenn Sie mit erfahrenen Software-Ingenieuren über ihre Kodierungs-, Migrations- oder Refactoring-Praktiken sprechen möchten, kontaktieren Sie uns. Wir freuen uns darauf, unsere Erfahrungen mit Ihnen zu diskutieren und Sie über effektive Lösungen für Ihr Projekt zu beraten. 

Kategorien Java


Dominik Wilczyński
Dominik Wilczyński Senior Java Developer
Mariola Nowak Content Writer

Design, Entwicklung, DevOps oder Cloud - welches Team brauchen Sie, um die Arbeit an Ihren Projekten zu beschleunigen?
Chatten Sie mit unseren Beratungspartnern, um herauszufinden, ob wir gut zusammenpassen.

Jakub Orczyk

Vorstandsmitglied /Verkaufsdirektor

Buchen Sie eine kostenlose Beratung
kuba (1)