Builder-Muster
Teil des Abschnitts Objektorientierte Programmierung der Java-Journey von Coddy. Lektion 67 von 87.
Das Builder Pattern ist ein erzeugendes Entwurfsmuster, das ein häufiges Problem löst: die Erstellung komplexer Objekte mit vielen optionalen Parametern. Anstatt mehrere Konstruktoren zu erstellen oder für nicht verwendete Parameter null zu übergeben, kannst du mit dem Builder Objekte Schritt für Schritt erstellen.
Stell dir vor, du erstellst ein User-Objekt mit erforderlichen Feldern (Name, E-Mail) und optionalen Feldern (Alter, Telefon, Adresse). Ohne einen Builder bräuchtest du verschachtelte Konstruktoren oder einen unübersichtlichen Konstruktor mit vielen Parametern. Das Builder-Muster bietet einen übersichtlicheren Ansatz:
public class User {
private final String name; // erforderlich
private final String email; // erforderlich
private final int age; // optional
private final String phone; // optional
private User(UserBuilder builder) {
this.name = builder.name;
this.email = builder.email;
this.age = builder.age;
this.phone = builder.phone;
}
public static class UserBuilder {
private final String name;
private final String email;
private int age;
private String phone;
public UserBuilder(String name, String email) {
this.name = name;
this.email = email;
}
public UserBuilder age(int age) {
this.age = age;
return this;
}
public UserBuilder phone(String phone) {
this.phone = phone;
return this;
}
public User build() {
return new User(this);
}
}
}Die Methoden des Builders geben this zurück und ermöglichen Methodenverkettung: eine flüssige, gut lesbare Möglichkeit, Objekte zu erstellen:
User user = new User.UserBuilder("Alice", "alice@email.com")
.age(25)
.phone("555-1234")
.build();Beachte, dass der Konstruktor von User privat ist: Objekte können nur über den Builder erstellt werden. Dieses Muster kommt besonders zur Geltung, wenn Objekte viele optionale Felder haben, und macht deinen Code lesbarer und weniger fehleranfällig als lange Parameterlisten.
Aufgabe
EinfachErstellen wir ein Pizza-Bestellsystem mit dem Builder Pattern! Statt mit einem Konstruktor zu arbeiten, der Dutzende von Parametern für alle möglichen Beläge und Optionen entgegennimmt, erstellst du einen Fluent Builder, der die Anpassung der Pizza übersichtlich und lesbar macht.
Du organisierst deinen Code über zwei Dateien:
Pizza.java: Erstelle einePizza-Klasse, die für ihre Erstellung das Builder Pattern verwendet. Deine Pizza sollte die folgenden Felder haben:size(String) – erforderlich, stellt die Pizzagröße darcheese(boolean) – optional, standardmäßig falsepepperoni(boolean) – optional, standardmäßig falsemushrooms(boolean) – optional, standardmäßig falseDer
Pizza-Konstruktor sollte privat sein und nur einenPizzaBuilderals Parameter akzeptieren, wobei alle Werte des Builders in die Felder der Pizza kopiert werden.Füge eine
getDescription()-Methode hinzu, die einen String in folgendem Format zurückgibt:[size] Pizza with: [toppings], wobei toppings eine durch Kommas getrennte Liste der Beläge ist, deren Wert true ist (Cheese, Pepperoni, Mushrooms). Wenn keine Beläge ausgewählt sind, sollte der toppings-Teil einfachno toppingslauten.Erstelle eine statische innere Klasse
PizzaBuildermit:- Einem Konstruktor, der den erforderlichen Parameter
sizeentgegennimmt - Methoden
cheese(),pepperoni()undmushrooms(), die jeweils den entsprechenden Boolean-Wert auf true setzen und für Verkettungenthiszurückgeben - Einer
build()-Methode, die eine neuePizzazurückgibt
- Einem Konstruktor, der den erforderlichen Parameter
Main.java: Zeige deinen Pizza Builder in Aktion! Du erhältst vier Eingaben: die Pizzagröße (String) und drei Ja/Nein-Werte (Strings), die angeben, ob Cheese, Pepperoni beziehungsweise Mushrooms hinzugefügt werden sollen.Erstelle mit der angegebenen Größe einen
PizzaBuilder. Verkette anschließend abhängig von jeder Ja/Nein-Eingabe die entsprechenden Belagmethoden. Wenn die Eingabe"yes"lautet, rufe die jeweilige Belagmethode auf; bei"no"überspringe sie. Rufe schließlichbuild()auf, um deine Pizza zu erstellen.Gib das Ergebnis des Aufrufs von
getDescription()für deine fertige Pizza aus.
Du erhältst vier Eingaben in dieser Reihenfolge: size (String), add cheese (String: "yes" oder "no"), add pepperoni (String: "yes" oder "no"), add mushrooms (String: "yes" oder "no").
Bei den Eingaben Large, yes, no, yes würde deine Ausgabe beispielsweise lauten: Large Pizza with: Cheese, Mushrooms
Beachte, wie das Builder Pattern es dir ermöglicht, Pizzen mit beliebigen Kombinationen von Belägen zu erstellen, ohne für jede Möglichkeit separate Konstruktoren zu benötigen!
Probier es selbst
import java.util.Scanner;
class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// Eingaben lesen
String size = scanner.nextLine();
String addCheese = scanner.nextLine();
String addPepperoni = scanner.nextLine();
String addMushrooms = scanner.nextLine();
// TODO: Erstelle einen PizzaBuilder mit der gegebenen Größe
// TODO: Verkette Topping-Methoden basierend auf Ja/Nein-Eingaben
// TODO: Baue die Pizza und gib ihre Beschreibung aus
}
}
Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.
Alle Lektionen in Objektorientierte Programmierung
1Grundlagen der OOP
Externe DateienEinführung in die OOPKlassen vs. ObjekteDas Schlüsselwort thisMethodenFelder (Attribute)KonstruktormethodeÜberladen von KonstruktorenRückblick – Einfacher Taschenrechner4Vererbung
Grundlegende Vererbung (extends)Das Schlüsselwort superÜberschreiben von Methoden (@Override)KonstruktorverkettungDie Object-KlasseEinfach- und mehrstufige VererbungWarum keine Mehrfachvererbung von KlassenRückblick – Mitarbeiterhierarchie7Spezielle Methoden & Object-Klasse
toString()-Methodeequals() und hashCode()clone()-MethodecompareTo() und ComparableComparator-SchnittstelleRückblick – Benutzerdefinierte Sortierung2Zugriffsmodifikatoren & Kapselung
Übersicht der ZugriffsebenenGetter- und Setter-MethodenInformationsverbergungDas Schlüsselwort finalRückblick – Bankkontoverwaltung5Polymorphismus
Grundlagen der MethodenüberladungMethodenüberschreibung (Laufzeit)Upcasting und DowncastingDer instanceof-OperatorAbstrakte Klassen und MethodenRückblick – Formrechner8Fortgeschrittene OOP-Konzepte
Komposition vs. VererbungAggregation vs. KompositionInnere, verschachtelte & anonyme KlassenEnums und Enum-MethodenRecords (Java 16+)Versiegelte Klassen (Java 17+)11Entwurfsmuster Teil 1
Einführung in EntwurfsmusterSingleton-MusterFactory-MusterBuilder-MusterObserver-MusterStrategie-Muster14Abschließende Herausforderungen
E-Learning-PlattformBankingsystemEntwicklung einer SpielfigurFahrzeugvermietungsdienst3Klassen-Props & statische Member
Instanz- vs. statische VariablenStatische MethodenStatische BlöckeKonstanten (static final)Wiederholung – Counter & Utility6Schnittstellen & abstrakte Klassen
Einführung in InterfacesInterfaces implementierenMehrere Interfaces implementierenDefault & Static in InterfacesAbstrakte Klassen vs. InterfacesFunktionale InterfacesRückblick – ZahlungssystemÜbe selbstständig: Online-Java-Compiler