Builder — podstawy wzorca budowniczego w programowaniu mobilnym

Autor: IT Sectr Opublikowano: 2026-02-17 Czas czytania: 7 min

Builder — wzorzec projektowy typu kreacyjnego, który pozwala tworzyć złożone obiekty krok po kroku. W przeciwieństwie do konstruktora z dziesiątką parametrów, Builder składa obiekt poprzez łańcuch wywołań, z których każdy ustawia jedno pole. Wzorzec jest szczególnie przydatny dla obiektów z wieloma opcjonalnymi parametrami: konfiguracja klienta sieciowego, ustawienia bazy danych, budownicze alertów i nawigacji. Więcej na Refactoring Guru: Builder.

Najważniejsze

  • Builder — stopniowe konstruowanie obiektów z oddzieleniem procesu od wyniku
  • Fluent interface — łańcuch wywołań set()/with() dla wygodnej konfiguracji
  • Niezmienność — Builder tworzy gotowy obiekt, który nie wymaga setterów
  • Wsteczna kompatybilność — nowe pola dodawane w Builder bez łamania klientów
  • Kotlin DSL vs Builder — Kotlin oferuje type-safe builders jako alternatywę

Czym jest Builder: istota wzorca budowniczego?

Builder (budowniczy) — kreacyjny wzorzec GoF, który oddziela konstruowanie złożonego obiektu od jego reprezentacji. Ten sam proces budowy może tworzyć różne reprezentacje. Builder jest przydatny, gdy obiekt ma wiele opcjonalnych parametrów, a konstruktor z dziesięcioma polami jest nieczytelny i nieelastyczny. Wzorzec rozwiązuje również problem Telescoping Constructor — antywzorca, gdzie liczba przeciążeń konstruktora rośnie wykładniczo.

Struktura Builder obejmuje wewnętrzną statyczną klasę Builder z polami kopiującymi pola głównej klasy. Każda metoda set zwraca Builder (this) dla fluent chaining. Finalna metoda build() tworzy docelowy obiekt, przekazując wartości pól do prywatnego konstruktora. Główna klasa ma prywatny konstruktor przyjmujący Builder. Klient: Object.builder().setField1(val1).setField2(val2).build().

Kiedy używać Builder — obiekty z 5+ polami, z których tylko 2-3 są wymagane. Obiekty konfiguracyjne (RequestConfig, DatabaseConfig). Obiekty ze złożoną logiką walidacji przy tworzeniu. Obiekty, które powinny być niezmienne (immutable) po utworzeniu. W Android Builder jest aktywnie używany w SDK: AlertDialog.Builder, Retrofit.Builder, OkHttpClient.Builder, NotificationCompat.Builder.

Builder w Kotlin: klasyczna i DSL implementacja

Kotlin Builder ma dwa podejścia: klasyczny Java-style Builder (przez klasę zagnieżdżoną) i Kotlin-style DSL builder (przez lambdę z receiverem). Java-style Builder jest preferowany dla kompatybilności z Androidem i przy użyciu z kodem Java. DSL builder — idiomatyczny sposób Kotlin: funkcja przyjmuje lambdę, wewnątrz której this jest kontekstem Builder, gdzie można bezpośrednio przypisywać pola.

kotlin
// Klasyczny Builder
data class HttpConfig private constructor(
    val baseUrl: String,
    val timeout: Long = 30_000,
    val retries: Int = 3,
    val headers: Map<String, String> = emptyMap()
) {
    class Builder {
        private var baseUrl: String = ""
        private var timeout: Long = 30_000
        private var retries: Int = 3
        private var headers: MutableMap<String, String> = mutableMapOf()

        fun baseUrl(url: String) = apply { this.baseUrl = url }
        fun timeout(ms: Long) = apply { this.timeout = ms }
        fun retries(n: Int) = apply { this.retries = n }
        fun header(key: String, value: String) = apply { headers[key] = value }

        fun build(): HttpConfig {
            require(baseUrl.isNotBlank()) { "baseUrl is required" }
            return HttpConfig(baseUrl, timeout, retries, headers)
        }
    }
}

// Użycie
val config = HttpConfig.Builder()
    .baseUrl("https://api.example.com")
    .timeout(15_000)
    .header("Authorization", "Bearer token")
    .build()

Kotlin DSL builder — alternatywa bez klasy zagnieżdżonej. Funkcja-builder przyjmuje lambdę w kontekście obiektu-budowniczego. Jest to idiomatyczne dla Kotlin i nie wymaga pól write. DSL builders są aktywnie używane w Ktor Client, kotlinx.serialization, Compose (Modifier). DSL builder jest niekompatybilny z Java i nie nadaje się do bibliotek z Java API.

Builder w Swift: result builders i łańcuchy

Swift Builder — Swift nie ma wbudowanego wzorca Builder, ale fluent interface można łatwo zaimplementować przez metody zwracające Self. Każda metoda ustawia właściwość i zwraca self. W przeciwieństwie do Kotlin, Swift nie wymaga osobnej klasy Builder — można zwracać sam obiekt, jeśli jest mutable na etapie składania. Dla immutable obiektów używa się zagnieżdżonej klasy Builder analogicznie do Kotlin.

swift
struct NetworkRequest {
    let url: String
    let method: HTTPMethod
    let headers: [String: String]
    let body: Data?
    let timeout: TimeInterval

    final class Builder {
        private var url: String = ""
        private var method: HTTPMethod = .get
        private var headers: [String: String] = [:]
        private var body: Data? = nil
        private var timeout: TimeInterval = 30

        func withURL(_: String) -> Self { /* self */ }
        func withMethod(_: HTTPMethod) -> Self { /* self */ }
        func withHeader(key: String, value: String) -> Self { /* self */ }
        func withBody(_: Data) -> Self { /* self */ }
        func withTimeout(_: TimeInterval) -> Self { /* self */ }

        func build() throws -> NetworkRequest {
            guard !url.isEmpty else { throw BuilderError.missingURL }
            return NetworkRequest(
                url: url, method: method, headers: headers,
                body: body, timeout: timeout
            )
        }
    }
}

Result Builders — Swift 5.4 wprowadził @resultBuilder — mechanizm językowy do deklaratywnego budowania struktur. SwiftUI, AttributedString, SceneBuilder używają result builders. To alternatywa dla klasycznego Builder: zamiast łańcucha set-metod result builder używa bloku kodu z elementami, które kompilator zbiera w tablicę lub drzewo. @ViewBuilder w SwiftUI — najbardziej znany przykład: wewnątrz body można pisać if, switch, ForEach, a kompilator buduje View z warunków.

Builder vs Telescoping Constructor: porównanie podejść

Telescoping Constructor — antywzorzec, przy którym klasa ma wiele przeciążonych konstruktorów z różnym zestawem parametrów. Na przykład trzy konstruktory: HttpConfig(url), HttpConfig(url, timeout), HttpConfig(url, timeout, retries). Wraz ze wzrostem parametrów liczba konstruktorów rośnie wykładniczo — dla n opcjonalnych pól potrzeba n! kombinacji. Builder rozwiązuje ten problem, pozwalając ustawiać tylko potrzebne pola.

CechaTelescoping ConstructorBuilderKotlin named args
Ilość koduWykładniczy wzrostLiniowy wzrostMinimalna
CzytelnośćNiska (który parametr?)Wysoka (metoda + nazwa)Wysoka (nazwa = wartość)
NiezmiennośćImmutableImmutableImmutable
Kompatybilność z JavaPełnaPełnaNie (tylko Kotlin)
WalidacjaW każdym konstruktorzeW build() — jeden razW init()

Kotlin named arguments + default values — elegancka alternatywa dla Builder w czystych projektach Kotlin. Parametry konstruktora mają wartości domyślne, klient przekazuje tylko potrzebne: HttpConfig(baseUrl = url, timeout = 15_000). Wadą jest brak możliwości walidacji wymaganych pól na etapie kompilacji. Builder daje wymagane pola przez konstruktor Builder (baseUrl jest wymagany). Dla bibliotek Java Builder pozostaje standardem de facto.

Builder w Android SDK: AlertDialog, Retrofit, OkHttp

Builder w Android SDK — jeden z najczęściej używanych wzorców w standardowej bibliotece. AlertDialog.Builder: new AlertDialog.Builder(context).setTitle().setMessage().setPositiveButton().create(). Retrofit.Builder: new Retrofit.Builder().baseUrl().addConverterFactory().build(). OkHttpClient.Builder: new OkHttpClient.Builder().connectTimeout().addInterceptor().build(). NotificationCompat.Builder: setContentTitle().setContentText().setSmallIcon().build().

Dlaczego Google używa Builder — wsteczna kompatybilność. Dodanie nowej metody w Builder nie łamie istniejącego kodu. Gdyby Google używał konstruktora z 20 parametrami, każde nowe pole wymagałoby nowego przeciążenia. Builder pozwala dodawać set-metody przez lata bez breaking changes. Na przykład NotificationCompat.Builder dodał setBubbleMetadata() w Android 11, nie wpływając na istniejący kod.

Builder w bibliotekach Kotlin — Ktor (HttpClientBuilder), Coil (ImageRequest.Builder), Room (Room.databaseBuilder()), Navigation (NavOptionsBuilder). W projektach Kotlin Builder często łączy się z DSL: Room.databaseBuilder(context, AppDatabase.class, "db").fallbackToDestructiveMigration().build(). Wzorzec pozostaje aktualny dla publicznych API, gdzie ważna jest wsteczna kompatybilność i interoperacyjność z Java.

Często zadawane pytania

Kiedy Builder jest zbędny?

Builder jest zbędny dla obiektów z 1-3 polami — zwykły konstruktor lub data class są czytelniejsze. Jest również zbędny w projektach Kotlin bez interoperacyjności z Java, gdzie named arguments + default values rozwiązują to samo zadanie prościej. Builder jest uzasadniony dla 5+ pól, złożonej walidacji lub Java API, gdzie named arguments są niedostępne.

Czym Builder różni się od Factory?

Builder tworzy jeden złożony obiekt krok po kroku (ustawianie pól), Factory tworzy obiekt w całości według typu lub parametrów. Builder odpowiada na pytanie «jak złożyć?», Factory — «co utworzyć?». Builder często łączy się z Factory: Factory wybiera typ, Builder ustawia pola.

Czy Builder jest potrzebny w SwiftUI?

W SwiftUI rolę Builder pełnią result builders (@ViewBuilder, @SceneBuilder) i modyfikatory View (.font(), .padding()). Klasyczny Builder nie jest wymagany, ponieważ SwiftUI używa deklaratywnego podejścia i fluent modifiers. Dla komponentów UIKit Builder jest przydatny: UIAlertController, URLRequest, NSAttributedString.

Jak uczynić Builder bezpiecznym wątkowo?

Builder zwykle nie wymaga bezpieczeństwa wątkowego, ponieważ jest używany w jednym wątku do składania obiektu. Jeśli Builder jest używany w środowisku wielowątkowym (rzadki przypadek), zsynchronizuj każdą set-metodę i build(). Alternatywą jest Immutable Builder: każda set-metoda zwraca nową instancję Builder ze zmienionym polem.

Dlaczego Retrofit używa Builder, a nie DI?

Retrofit.Builder to publiczne API biblioteki, które musi działać bez kontenera DI. Builder daje elastyczność konfiguracji (baseUrl, konwertery, interceptory, niestandardowe call adapters) bez zależności od Dagger lub innych DI. Wewnątrz aplikacji DI może utworzyć Retrofit raz przez Builder, ale sam Builder pozostaje częścią publicznego API Retrofit.

Podsumowanie

  • Builder — stopniowe konstruowanie obiektów z fluent interface
  • Kotlin Builder — klasyczny (zagnieżdżona klasa) i DSL (lambda z receiverem)
  • Swift Builder — zagnieżdżona klasa lub @resultBuilder dla kodu deklaratywnego
  • Niezmienność — Builder tworzy immutable obiekty przez prywatny konstruktor
  • Android SDK — AlertDialog, Retrofit, OkHttp, NotificationCompat — standard branży
  • Wsteczna kompatybilność — dodawanie pól w Builder nie łamie istniejącego kodu
  • Alternatywa Kotlin — named arguments + default values prostsze dla czystych projektów Kotlin

Opracujemy aplikację mobilną pod klucz

IT Sectr tworzy aplikacje na iOS i Androida dla startupów i firm od 2017 roku. Doradzimy Ci i zaproponujemy najlepsze rozwiązanie.

Omów projekt

Przeczytaj również