Wzorzec Builder z płynnym API
Twórz złożone obiekty krok po kroku za pomocą płynnego buildera, aby uniknąć rozbudowanych konstruktorów.
Wzorzec Builder z płynnym API to bezpłatna lekcja Java Academy na CoddyKit. To lekcja 4 z 4. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej Java Academy, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs Java Academy zawiera 4 lekcji w sumie.
Problem teleskopowego konstruktora
Gdy klasa ma wiele opcjonalnych parametrów, liczba konstruktorów szybko rośnie: konstruktor z 2, 3, 4 parametrami i tak dalej. Wzorzec Builder rozwiązuje ten problem za pomocą płynnego interfejsu API do konfiguracji krok po kroku.
// Without builder — hard to read:
Pizza p = new Pizza("large", "thin", true, false, true, false, "mozzarella");Podstawowa struktura Builder
Należy utworzyć statyczną zagnieżdżoną klasę Builder. Klasa zewnętrzna powinna mieć prywatny konstruktor przyjmujący obiekt buildera. Każdy setter w builderze powinien zwracać this, umożliwiając płynne łączenie wywołań.
public final class Pizza {
private final String size, crustType, cheese;
private final boolean extraSauce, pepperoni;
private Pizza(Builder b) {
this.size = b.size; this.crustType = b.crustType;
this.cheese = b.cheese; this.extraSauce = b.extraSauce;
this.pepperoni = b.pepperoni;
}
public static class Builder {
private final String size;
private String crustType = "regular", cheese = "mozzarella";
private boolean extraSauce, pepperoni;
public Builder(String size) { this.size = size; }
public Builder crustType(String c) { this.crustType = c; return this; }
public Builder cheese(String c) { this.cheese = c; return this; }
public Builder extraSauce() { this.extraSauce = true; return this; }
public Builder pepperoni() { this.pepperoni = true; return this; }
public Pizza build() { return new Pizza(this); }
}
}Korzystanie z płynnego interfejsu API
Builder przypomina zdanie. Parametry wymagane należy przekazać do konstruktora, a opcjonalne ustawiać za pomocą wywołań metod. Końcowe wywołanie build() tworzy niezmienny obiekt.
Pizza p = new Pizza.Builder("large")
.crustType("thin")
.extraSauce()
.pepperoni()
.build();
System.out.println(p);Walidacja w build()
Przed utworzeniem obiektu należy dodać logikę walidacji wewnątrz build(). W przypadku nieprawidłowych kombinacji należy zgłosić IllegalStateException lub IllegalArgumentException.
public Pizza build() {
if (size == null || size.isBlank())
throw new IllegalArgumentException("Size required");
if (extraSauce && crustType.equals("stuffed"))
throw new IllegalStateException("No extra sauce on stuffed crust");
return new Pizza(this);
}Builder dla niezmiennych obiektów
Ponieważ klasa zewnętrzna jest tworzona z buildera za jednym razem, wszystkie pola mogą być oznaczone jako final — dzięki temu wynikowy obiekt będzie w pełni niezmienny i bezpieczny wątkowo.
public final class Address {
private final String street, city, country;
private final String postalCode;
private Address(Builder b) { ... } // all finals
// no setters — immutable!
}Generyczny Builder z typem własnym
W hierarchiach dziedziczenia należy użyć rekurencyjnego parametru typu generycznego (SELF extends Builder<SELF>), aby buildery podklas zwracały typ podklasy w łańcuchach wywołań metod.
public abstract static class Builder<SELF extends Builder<SELF>> {
String name;
@SuppressWarnings("unchecked")
public SELF name(String n) { this.name = n; return (SELF) this; }
public abstract Vehicle build();
}Lombok @Builder
Adnotacja @Builder biblioteki Lombok generuje całą infrastrukturę buildera w czasie kompilacji. Należy użyć @Builder.Default dla domyślnych wartości pól oraz @Singular dla kolekcji.
@Builder
public class Report {
private final String title;
@Builder.Default private final int pageCount = 1;
@Singular private final List<String> authors;
}
// Usage:
Report r = Report.builder().title("Q4").author("Alice").author("Bob").build();Builder w JDK
Wiele klas JDK korzysta z interfejsów API w stylu buildera: StringBuilder, HttpRequest.newBuilder(), ProcessBuilder, Stream.Builder. Warto rozpoznawać ten wzorzec.
HttpRequest req = HttpRequest.newBuilder()
.uri(URI.create("https://api.example.com/users"))
.header("Accept", "application/json")
.GET()
.build();Builder a Factory
Builder należy stosować, gdy obiekt wymaga wielu parametrów i konfiguracji krok po kroku. Factory należy stosować, gdy wszystkie warianty są proste, a utworzenie obiektu można wykonać jednym wywołaniem.
Builder dla danych testowych
Buildery świetnie sprawdzają się w testach: w pomocniczej metodzie należy utworzyć bazowy builder, a następnie nadpisywać tylko pola istotne dla danego przypadku testowego — dzięki temu testy pozostają zgodne z zasadą DRY i czytelne.
User defaultUser() { return new User.Builder("test@example.com").name("Test User").build(); }
User adminUser() { return new User.Builder("admin@example.com").name("Admin").role(ADMIN).build(); }Bezpieczeństwo wątkowe
Sam builder nie jest bezpieczny wątkowo — nie należy współdzielić obiektu Builder między wątkami. Zbudowany z niego produkt może być niezmienny i bezpieczny wątkowo, jeśli wszystkie pola są finalne i nie są współdzielone żadne obiekty zmienne.
Szybki test
Co sprawia, że metoda buildera jest „fluent”?
Podsumowanie
Builder rozwiązuje problem teleskopowego konstruktora. Należy użyć statycznej zagnieżdżonej klasy Builder, zwracać this z każdego settera, przeprowadzać walidację w build() i tworzyć niezmienny obiekt. Lombok @Builder automatyzuje kod pomocniczy.
Często zadawane pytania
Czy lekcja „Wzorzec Builder z płynnym API” jest bezpłatna?
Tak — pełny tekst „Wzorzec Builder z płynnym API” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu Java Academy, przejdź na CoddyKit PRO. Kurs Java Academy zawiera 4 lekcji w sumie.
Co nauczysz się w „Wzorzec Builder z płynnym API”?
Twórz złożone obiekty krok po kroku za pomocą płynnego buildera, aby uniknąć rozbudowanych konstruktorów. Ćwiczysz Java Academy z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.
Czy potrzebuję doświadczenia, aby zacząć Java Academy?
Nie wymagamy żadnego doświadczenia. Java Academy w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 4 z 4.
Ile czasu zajmuje lekcja „Wzorzec Builder z płynnym API”?
Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.
Czy mogę pisać i uruchamiać kod w tej lekcji Java Academy?
Tak. Każda lekcja Java Academy zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.
Wszystkie lekcje w tym kursie
- Singleton: implementacje bezpieczne wątkowo
- Wzorzec Factory Method
- Abstract Factory dla rodzin produktów
- Wzorzec Builder z płynnym API