Builder — основи на модела строител в мобилната разработка

Автор: IT Sectr Публикувано: 2026-02-17 Време за четене: 7 мин

Builder — създаващ модел, който позволява създаването на сложни обекти стъпка по стъпка. За разлика от конструктор с десет параметъра, Builder сглобява обекта чрез верига от извиквания, всяко от които настройва едно поле. Моделът е особено полезен за обекти с множество опционални параметри: конфигурация на мрежов клиент, настройки на база данни, строители на alert-ове и навигация. Повече на Refactoring Guru: Builder.

Основни неща

  • Builder — поетапно конструиране на обекти с разделяне на процеса и резултата
  • Fluent interface — верига от извиквания set()/with() за удобна настройка
  • Неизменяемост — Builder създава готов обект, който не изисква setter-и
  • Обратна съвместимост — нови полета се добавят в Builder без да се нарушават клиентите
  • Kotlin DSL vs Builder — Kotlin предлага type-safe builders като алтернатива

Какво е Builder: същност на модела строител?

Builder (строител) — създаващ GoF модел, който отделя конструирането на сложен обект от неговото представяне. Един и същ процес на изграждане може да създава различни представяния. Builder е полезен, когато обектът има много опционални параметри, а конструктор с десет полета е нечетим и негъвкав. Моделът също така решава проблема с Telescoping Constructor — анти-модел, при който броят на претоварванията на конструктора расте експоненциално.

Структура на Builder включва вътрешен статичен клас Builder с полета, копиращи полетата на основния клас. Всеки set-метод връща Builder (this) за fluent chaining. Финалният build() метод създава целевия обект, предавайки стойностите на полетата на частния конструктор. Основният клас има частен конструктор, приемащ Builder. Клиент: Object.builder().setField1(val1).setField2(val2).build().

Кога да използваме Builder — обекти с 5+ полета, от които само 2-3 задължителни. Конфигурационни обекти (RequestConfig, DatabaseConfig). Обекти със сложна логика за валидация при създаване. Обекти, които трябва да бъдат неизменяеми (immutable) след създаване. В Android Builder се използва активно в SDK: AlertDialog.Builder, Retrofit.Builder, OkHttpClient.Builder, NotificationCompat.Builder.

Builder в Kotlin: класическа и DSL реализация

Kotlin Builder има два подхода: класически Java-style Builder (чрез вложен клас) и Kotlin-style DSL builder (чрез ламбда с receiver). Java-style Builder е предпочитан за съвместимост с Android и при използване с Java код. DSL builder — идиоматичният Kotlin начин: функцията приема ламбда, вътре в която this е контекстът на Builder, където полетата могат да се присвояват директно.

kotlin
// Класически 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)
        }
    }
}

// Използване
val config = HttpConfig.Builder()
    .baseUrl("https://api.example.com")
    .timeout(15_000)
    .header("Authorization", "Bearer token")
    .build()

Kotlin DSL builder — алтернатива без вложен клас. Функцията-builder приема ламбда в контекста на обекта-строител. Това е идиоматично за Kotlin и не изисква write-полета. DSL builders се използват активно в Ktor Client, kotlinx.serialization, Compose (Modifier). DSL builder е несъвместим с Java и не е подходящ за библиотеки с Java API.

Builder в Swift: result builders и вериги

Swift Builder — Swift няма вграден Builder модел, но fluent interface лесно се реализира чрез методи, връщащи Self. Всеки метод настройва свойство и връща self. За разлика от Kotlin, Swift не изисква отделен Builder клас — може да се върне самият обект, ако е mutable на етапа на сглобяване. За immutable обекти се използва вложен Builder клас по аналогия с 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 въведе @resultBuilder — езиков механизъм за декларативно изграждане на структури. SwiftUI, AttributedString, SceneBuilder използват result builders. Това е алтернатива на класическия Builder: вместо верига от set-методи, result builder използва блок код с елементи, които компилаторът събира в масив или дърво. @ViewBuilder в SwiftUI — най-известният пример: в body могат да се пишат if, switch, ForEach, и компилаторът изгражда View от условията.

Builder vs Telescoping Constructor: сравнение на подходите

Telescoping Constructor — анти-модел, при който класът има множество претоварени конструктори с различни набори от параметри. Например три конструктора: HttpConfig(url), HttpConfig(url, timeout), HttpConfig(url, timeout, retries). С нарастването на параметрите броят на конструкторите расте експоненциално — за n опционални полета са необходими n! комбинации. Builder решава този проблем, позволявайки настройка само на необходимите полета.

ХарактеристикаTelescoping ConstructorBuilderKotlin named args
Количество кодЕкспоненциален растежЛинеен растежМинимално
ЧетимостНиска (кой параметър?)Висока (метод + име)Висока (име = стойност)
НеизменяемостImmutableImmutableImmutable
Java съвместимостПълнаПълнаНе (само Kotlin)
ВалидацияВъв всеки конструкторВ build() — веднъжВ init()

Kotlin named arguments + default values — елегантна алтернатива на Builder в чисти Kotlin проекти. Параметрите на конструктора имат стойности по подразбиране, клиентът предава само необходимите: HttpConfig(baseUrl = url, timeout = 15_000). Недостатък — невъзможност за валидация на задължителните полета на етапа на компилация. Builder дава задължителните полета чрез конструктора на Builder (baseUrl е задължителен). За Java библиотеки Builder остава де факто стандарт.

Builder в Android SDK: AlertDialog, Retrofit, OkHttp

Builder в Android SDK — един от най-разпространените модели в стандартната библиотека. 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().

Защо Google използва Builder — обратна съвместимост. Добавянето на нов метод в Builder не нарушава съществуващия код. Ако Google използваше конструктор с 20 параметъра, всяко ново поле щеше да изисква ново претоварване. Builder позволява добавяне на set-методи в продължение на години без breaking changes. Например NotificationCompat.Builder добави setBubbleMetadata() в Android 11, без да засегне съществуващия код.

Builder в Kotlin библиотеки — Ktor (HttpClientBuilder), Coil (ImageRequest.Builder), Room (Room.databaseBuilder()), Navigation (NavOptionsBuilder). В Kotlin проекти Builder често се комбинира с DSL: Room.databaseBuilder(context, AppDatabase.class, "db").fallbackToDestructiveMigration().build(). Моделът остава актуален за публични API, където важни са обратната съвместимост и Java интероперабилността.

Често задавани въпроси

Кога Builder е излишен?

Builder е излишен за обекти с 1-3 полета — обикновен конструктор или data class са по-разбираеми. Също така е излишен в Kotlin проекти без Java интероп, където named arguments + default values решават същата задача по-просто. Builder е оправдан за 5+ полета, сложна валидация или Java API, където named arguments не са достъпни.

С какво Builder се различава от Factory?

Builder създава един сложен обект стъпка по стъпка (настройка на полета), Factory създава обект изцяло по тип или параметри. Builder отговаря на въпроса «как да сглобим?», Factory — «какво да създадем?». Builder често се комбинира с Factory: Factory избира типа, Builder настройва полетата.

Нужен ли е Builder в SwiftUI?

В SwiftUI ролята на Builder се изпълнява от result builders (@ViewBuilder, @SceneBuilder) и модификаторите на View (.font(), .padding()). Класическият Builder не е необходим, тъй като SwiftUI използва декларативен подход и fluent modifiers. За UIKit компоненти Builder е полезен: UIAlertController, URLRequest, NSAttributedString.

Как да направим Builder thread-safe?

Builder обикновено не изисква безопасност на нишките, тъй като се използва в една нишка за сглобяване на обекта. Ако Builder се използва в многонишкова среда (рядък случай), синхронизирайте всеки set-метод и build(). Алтернатива — Immutable Builder: всеки set-метод връща нова инстанция на Builder с промененото поле.

Защо Retrofit използва Builder, а не DI?

Retrofit.Builder е публичният API на библиотеката, който трябва да работи без DI контейнер. Builder дава гъвкавост на конфигурацията (baseUrl, конвертори, интерцептори, персонализирани call adapter-и) без зависимости от Dagger или други DI. Вътре в приложението DI може да създаде Retrofit веднъж чрез Builder, но самият Builder остава част от публичния API на Retrofit.

Обобщение

  • Builder — поетапно конструиране на обекти с fluent interface
  • Kotlin Builder — класически (вложен клас) и DSL (ламбда с receiver)
  • Swift Builder — вложен клас или @resultBuilder за декларативен код
  • Неизменяемост — Builder създава immutable обекти чрез частен конструктор
  • Android SDK — AlertDialog, Retrofit, OkHttp, NotificationCompat — индустриален стандарт
  • Обратна съвместимост — добавянето на полета в Builder не нарушава съществуващия код
  • Алтернатива Kotlin — named arguments + default values по-прости за чисти Kotlin проекти

Ще разработим мобилно приложение под ключ

IT Sectr създава iOS и Android приложения за стартъпи и бизнеси от 2017 г. Ще ви консултираме и ще предложим най-доброто решение.

Обсъдете проекта

Прочетете също