0Pricing
Java Academy · Lekcja

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

  1. Singleton: implementacje bezpieczne wątkowo
  2. Wzorzec Factory Method
  3. Abstract Factory dla rodzin produktów
  4. Wzorzec Builder z płynnym API
← Powrót do Java Academy