Einführung in Kreationsmuster

1. Einleitung

In der Softwareentwicklung beschreibt ein Entwurfsmuster eine etablierte Lösung für die am häufigsten auftretenden Probleme beim Softwareentwurf. Es stellt die Best Practices dar, die über einen langen Zeitraum durch Ausprobieren erfahrener Softwareentwickler entwickelt wurden.

Design Patterns wurden immer beliebter, nachdem das Buch Design Patterns: Elemente wiederverwendbarer objektorientierter Software 1994 von Erich Gamma, John Vlissides, Ralph Johnson und Richard Helm (auch bekannt als Gang of Four oder GoF) veröffentlicht wurde.

In diesem Artikel werden wir uns mit kreativen Designmustern und ihren Typen befassen. Wir werden uns auch einige Codebeispiele ansehen und die Situationen diskutieren, in denen diese Muster zu unserem Design passen.

2. Kreative Entwurfsmuster

Creational Design Patterns befassen sich mit der Art und Weise, wie Objekte erstellt werden. Sie reduzieren Komplexität und Instabilität, indem sie Objekte auf kontrollierte Weise erstellen.

Der neue Bediener wird häufig als schädlich angesehen, da er Objekte in der gesamten Anwendung streut. Im Laufe der Zeit kann es schwierig werden, eine Implementierung zu ändern, da die Klassen eng miteinander verbunden sind.

Creational Design Patterns beheben dieses Problem, indem sie den Client vollständig vom eigentlichen Initialisierungsprozess entkoppeln.

In diesem Artikel werden vier Arten von kreativen Entwurfsmustern erläutert:

  1. Singleton - Stellt sicher, dass in der gesamten Anwendung höchstens eine Instanz eines Objekts vorhanden ist
  2. Factory-Methode - Erstellt Objekte mehrerer verwandter Klassen, ohne das genaue zu erstellende Objekt anzugeben
  3. Abstrakte Fabrik - Erstellt Familien verwandter abhängiger Objekte
  4. Builder - Konstruiert komplexe Objekte Schritt für Schritt

Lassen Sie uns nun jedes dieser Muster im Detail diskutieren.

3. Singleton-Entwurfsmuster

Das Singleton-Entwurfsmuster zielt darauf ab, die Initialisierung von Objekten einer bestimmten Klasse zu überprüfen, indem sichergestellt wird, dass in der gesamten Java Virtual Machine nur eine Instanz des Objekts vorhanden ist.

Eine Singleton-Klasse stellt außerdem einen eindeutigen globalen Zugriffspunkt für das Objekt bereit, sodass bei jedem nachfolgenden Aufruf des Zugriffspunkts nur dieses bestimmte Objekt zurückgegeben wird.

3.1. Beispiel für ein Singleton-Muster

Obwohl das Singleton-Muster von GoF eingeführt wurde, ist bekannt, dass die ursprüngliche Implementierung in Multithread-Szenarien problematisch ist.

Hier verfolgen wir also einen optimaleren Ansatz, der eine statische innere Klasse verwendet:

public class Singleton { private Singleton() {} private static class SingletonHolder { public static final Singleton instance = new Singleton(); } public static Singleton getInstance() { return SingletonHolder.instance; } }

Hier haben wir eine statische innere Klasse erstellt, die die Instanz der Singleton- Klasse enthält. Die Instanz wird nur erstellt, wenn jemand die Methode getInstance () aufruft und nicht, wenn die äußere Klasse geladen wird.

Dies ist ein weit verbreiteter Ansatz für eine Singleton-Klasse, da sie keine Synchronisation erfordert, threadsicher ist, eine verzögerte Initialisierung erzwingt und vergleichsweise weniger Boilerplate hat.

Beachten Sie außerdem, dass der Konstruktor über den Modifikator für den privaten Zugriff verfügt. Dies ist eine Voraussetzung für die Erstellung eines Singleton, da ein öffentlicher Konstruktor bedeuten würde, dass jeder darauf zugreifen und mit der Erstellung neuer Instanzen beginnen kann.

Denken Sie daran, dass dies nicht die ursprüngliche GoF-Implementierung ist. Für die Originalversion besuchen Sie bitte diesen verlinkten Baeldung-Artikel über Singletons in Java.

3.2. Wann wird das Singleton-Entwurfsmuster verwendet?

  • Für Ressourcen, deren Erstellung teuer ist (z. B. Datenbankverbindungsobjekte)
  • Es wird empfohlen, alle Logger als Singletons zu behalten, um die Leistung zu steigern
  • Klassen, die Zugriff auf Konfigurationseinstellungen für die Anwendung bieten
  • Klassen, die Ressourcen enthalten, auf die im freigegebenen Modus zugegriffen wird

4. Entwurfsmuster für die Werksmethode

Das Factory Design Pattern oder Factory Method Design Pattern ist eines der am häufigsten verwendeten Designmuster in Java.

Laut GoF definiert dieses Muster „eine Schnittstelle zum Erstellen eines Objekts, lässt jedoch Unterklassen entscheiden, welche Klasse instanziiert werden soll. Mit der Factory-Methode kann eine Klasse die Instanziierung auf Unterklassen verschieben. “

Dieses Muster delegiert die Verantwortung für die Initialisierung einer Klasse vom Client an eine bestimmte Factory-Klasse, indem ein Typ eines virtuellen Konstruktors erstellt wird.

Um dies zu erreichen, verlassen wir uns auf eine Fabrik, die uns die Objekte zur Verfügung stellt und die tatsächlichen Implementierungsdetails verbirgt. Auf die erstellten Objekte wird über eine gemeinsame Schnittstelle zugegriffen.

4.1. Beispiel für ein Entwurfsmuster für die Werksmethode

In diesem Beispiel erstellen wir eine Polygon- Schnittstelle, die von mehreren konkreten Klassen implementiert wird. Eine PolygonFactory wird verwendet, um Objekte aus dieser Familie abzurufen:

Lassen Sie uns zuerst die Polygon- Oberfläche erstellen :

public interface Polygon { String getType(); }

Als Nächstes erstellen wir einige Implementierungen wie Square , Triangle usw., die diese Schnittstelle implementieren und ein Objekt vom Typ Polygon zurückgeben .

Jetzt können wir eine Factory erstellen, die die Anzahl der Seiten als Argument verwendet und die entsprechende Implementierung dieser Schnittstelle zurückgibt:

public class PolygonFactory { public Polygon getPolygon(int numberOfSides) { if(numberOfSides == 3) { return new Triangle(); } if(numberOfSides == 4) { return new Square(); } if(numberOfSides == 5) { return new Pentagon(); } if(numberOfSides == 7) { return new Heptagon(); } else if(numberOfSides == 8) { return new Octagon(); } return null; } }

Beachten Sie, wie sich der Client auf diese Factory verlassen kann, um uns ein geeignetes Polygon zu geben , ohne das Objekt direkt initialisieren zu müssen.

4.2. Wann wird das Entwurfsmuster für die Werksmethode verwendet?

  • Wenn erwartet wird, dass sich die Implementierung einer Schnittstelle oder einer abstrakten Klasse häufig ändert
  • Wenn die aktuelle Implementierung neue Änderungen nicht bequem aufnehmen kann
  • Wenn der Initialisierungsprozess relativ einfach ist und der Konstruktor nur eine Handvoll Parameter benötigt

5. Abstract Factory Design Pattern

In the previous section, we saw how the Factory Method design pattern could be used to create objects related to a single family.

By contrast, the Abstract Factory Design Pattern is used to create families of related or dependent objects. It's also sometimes called a factory of factories.

For a detailed explanation, check out our Abstract Factory tutorial.

6. Builder Design Pattern

The Builder Design Pattern is another creational pattern designed to deal with the construction of comparatively complex objects.

When the complexity of creating object increases, the Builder pattern can separate out the instantiation process by using another object (a builder) to construct the object.

This builder can then be used to create many other similar representations using a simple step-by-step approach.

6.1. Builder Pattern Example

The original Builder Design Pattern introduced by GoF focuses on abstraction and is very good when dealing with complex objects, however, the design is a little complicated.

Joshua Bloch, in his book Effective Java, introduced an improved version of the builder pattern which is clean, highly readable (because it makes use of fluent design) and easy to use from client's perspective. In this example, we'll discuss that version.

This example has only one class, BankAccount which contains a builder as a static inner class:

public class BankAccount { private String name; private String accountNumber; private String email; private boolean newsletter; // constructors/getters public static class BankAccountBuilder { // builder code } } 

Note that all the access modifiers on the fields are declared private since we don't want outer objects to access them directly.

The constructor is also private so that only the Builder assigned to this class can access it. All of the properties set in the constructor are extracted from the builder object which we supply as an argument.

We've defined BankAccountBuilder in a static inner class:

public static class BankAccountBuilder { private String name; private String accountNumber; private String email; private boolean newsletter; public BankAccountBuilder(String name, String accountNumber) { this.name = name; this.accountNumber = accountNumber; } public BankAccountBuilder withEmail(String email) { this.email = email; return this; } public BankAccountBuilder wantNewsletter(boolean newsletter) { this.newsletter = newsletter; return this; } public BankAccount build() { return new BankAccount(this); } } 

Notice we've declared the same set of fields that the outer class contains. Any mandatory fields are required as arguments to the inner class's constructor while the remaining optional fields can be specified using the setter methods.

This implementation also supports the fluent design approach by having the setter methods return the builder object.

Finally, the build method calls the private constructor of the outer class and passes itself as the argument. The returned BankAccount will be instantiated with the parameters set by the BankAccountBuilder.

Let's see a quick example of the builder pattern in action:

BankAccount newAccount = new BankAccount .BankAccountBuilder("Jon", "22738022275") .withEmail("[email protected]") .wantNewsletter(true) .build();

6.2. When to Use Builder Pattern

  1. When the process involved in creating an object is extremely complex, with lots of mandatory and optional parameters
  2. When an increase in the number of constructor parameters leads to a large list of constructors
  3. When client expects different representations for the object that's constructed

7. Conclusion

In diesem Artikel haben wir etwas über kreative Entwurfsmuster in Java gelernt. Wir haben auch ihre vier verschiedenen Typen besprochen, dh Singleton, Factory-Methode, Abstract Factory und Builder-Muster, ihre Vorteile, Beispiele und wann wir sie verwenden sollten.

Wie immer sind die vollständigen Codefragmente auf GitHub verfügbar.