Builder — стваралачки образац који омогућава креирање сложених објеката корак по корак. За разлику од конструктора са десет параметара, Builder саставља објекат кроз ланац позива, од којих сваки подешава једно поље. Образац је посебно користан за објекте са много опционих параметара: конфигурација мрежног клијента, подешавања базе података, градитељи алертова и навигације. Више на Refactoring Guru: 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.
Kotlin Builder има два приступа: класични Java-style Builder (преко угњеждене класе) и Kotlin-style DSL builder (преко ламбде са ресивером). Java-style Builder је пожељан за компатибилност са Android-ом и при коришћењу са Java кодом. DSL builder — идиоматски Kotlin начин: функција прима ламбду, унутар које this представља Builder контекст, где се поља могу директно додељивати.
// Класични 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 — алтернатива без угњеждене класе. Функција-градитељ прима ламбду у контексту објекта-градитеља. Ово је идиоматски за Kotlin и не захтева write-поља. DSL builders се активно користе у Ktor Client, kotlinx.serialization, Compose (Modifier). DSL builder није компатибилан са Java и није погодан за библиотеке са Java API.
Swift Builder — Swift нема уграђени Builder образац, али fluent interface се лако имплементира кроз методе које враћају Self. Свака метода подешава својство и враћа self. За разлику од Kotlin-а, Swift не захтева посебну Builder класу — може се враћати сам објекат ако је mutable у фази склапања. За immutable објекте користи се угњеждена Builder класа по аналогији са Kotlin-ом.
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 из услова.
Telescoping Constructor — антиобразац где класа има више преоптерећених конструктора са различитим сетовима параметара. На пример, три конструктора: HttpConfig(url), HttpConfig(url, timeout), HttpConfig(url, timeout, retries). Са порастом параметара број конструктора расте експоненцијално — за n опционих поља потребно је n! комбинација. Builder решава овај проблем омогућавајући подешавање само потребних поља.
| Карактеристика | Telescoping Constructor | Builder | Kotlin named args |
|---|---|---|---|
| Количина кода | Експоненцијални раст | Линеарни раст | Минимална |
| Читљивост | Ниска (који параметар?) | Висока (метод + име) | Висока (име = вредност) |
| Непроменљивост | Immutable | Immutable | Immutable |
| 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.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 је сувишан за објекте са 1-3 поља — обичан конструктор или data class су разумљивији. Такође је сувишан у Kotlin пројектима без Java интеропа, где named arguments + default values решавају исти задатак једноставније. Builder је оправдан за 5+ поља, сложену валидацију или Java API где named arguments нису доступни.
Builder ствара један сложен објекат корак по корак (подешавање поља), Factory ствара објекат у целини по типу или параметрима. Builder одговара на питање «како саставити?», Factory — «шта створити?». Builder се често комбинује са Factory: Factory бира тип, Builder подешава поља.
У SwiftUI улогу Builder-а обављају result builders (@ViewBuilder, @SceneBuilder) и модификатори View (.font(), .padding()). Класични Builder није потребан јер SwiftUI користи декларативни приступ и fluent modifiers. За UIKit компоненте Builder је користан: UIAlertController, URLRequest, NSAttributedString.
Builder обично не захтева безбедност нити, јер се користи у једној нити за склапање објекта. Ако се Builder користи у вишенитном окружењу (редак случај), синхронизујте сваку set-методу и build(). Алтернатива — Immutable Builder: свака set-метода враћа нову инстанцу Builder-а са измењеним пољем.
Retrofit.Builder — јавни API библиотеке који мора да ради без DI контејнера. Builder даје флексибилност подешавања (baseUrl, конвертори, интерцептори, прилагођени call adapterи) без зависности од Dagger-а или других DI. Унутар апликације DI може креирати Retrofit једном кроз Builder, али сам Builder остаје део јавног API-ја Retrofit-а.
Закључци
Развићемо мобилну апликацију под кључ
IT Sectr креира iOS и Android апликације за стартапе и предузећа од 2017. године. Саветоваћемо вас и предложити најбоље решење.
Прочитајте такође