Builder — fondamenti del modello builder nello sviluppo mobile

Autore: IT Sectr Pubblicato: 2026-02-17 Tempo di lettura: 7 min

Builder — un modello creazionale che consente di creare oggetti complessi passo dopo passo. A differenza di un costruttore con una dozzina di parametri, Builder assembla un oggetto attraverso una catena di chiamate, ciascuna delle quali configura un campo. Il modello è particolarmente utile per oggetti con molti parametri opzionali: configurazione del client di rete, impostazioni del database, costruttori di avvisi e navigazione. Maggiori informazioni su Refactoring Guru: Builder.

Punti chiave

  • Builder — costruzione passo passo degli oggetti separando processo e risultato
  • Fluent interface — catena di chiamate set()/with() per una configurazione comoda
  • Immutabilità — Builder crea un oggetto pronto che non richiede setter
  • Retrocompatibilità — nuovi campi possono essere aggiunti a Builder senza rompere i client
  • Kotlin DSL vs Builder — Kotlin offre type-safe builders come alternativa

Cos'è Builder: l'essenza del modello costruttore?

Builder — un modello creazionale GoF che separa la costruzione di un oggetto complesso dalla sua rappresentazione. Lo stesso processo di costruzione può creare rappresentazioni diverse. Builder è utile quando un oggetto ha molti parametri opzionali e un costruttore con dieci campi è illeggibile e inflessibile. Il modello risolve anche l'anti-modello Telescoping Constructor, dove il numero di sovraccarichi del costruttore cresce esponenzialmente.

Struttura di Builder include una classe interna statica Builder con campi che rispecchiano i campi della classe principale. Ogni set-metodo restituisce Builder (this) per un concatenamento fluido. Il metodo finale build() crea l'oggetto target passando i valori dei campi a un costruttore privato. La classe principale ha un costruttore privato che accetta un Builder. Client: Object.builder().setField1(val1).setField2(val2).build().

Quando usare Builder — oggetti con 5+ campi di cui solo 2-3 obbligatori. Oggetti di configurazione (RequestConfig, DatabaseConfig). Oggetti con logica di validazione complessa durante la creazione. Oggetti che devono essere immutabili dopo la creazione. In Android, Builder è attivamente utilizzato nell'SDK: AlertDialog.Builder, Retrofit.Builder, OkHttpClient.Builder, NotificationCompat.Builder.

Builder in Kotlin: implementazione classica e DSL

Builder in Kotlin ha due approcci: Builder classico stile Java (tramite una classe annidata) e Builder DSL stile Kotlin (tramite una lambda con ricevitore). Il Builder stile Java è preferibile per la compatibilità con Android e quando utilizzato con codice Java. Il DSL builder è il modo idiomatico di Kotlin: una funzione accetta una lambda all'interno della quale this è il contesto Builder dove i campi possono essere assegnati direttamente.

kotlin
// Builder classico
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)
        }
    }
}

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

Kotlin DSL builder — un'alternativa senza classe annidata. Una funzione builder accetta una lambda nel contesto di un oggetto costruttore. Questo è idiomatico per Kotlin e non richiede write-campi. I DSL builders sono attivamente utilizzati in Ktor Client, kotlinx.serialization, Compose (Modifier). Il DSL builder è incompatibile con Java e non è adatto per librerie con API Java.

Builder in Swift: result builders e catene

Builder in Swift — Swift non ha un modello Builder integrato, ma un'interfaccia fluida è facilmente implementata tramite metodi che restituiscono Self. Ogni metodo configura una proprietà e restituisce self. A differenza di Kotlin, Swift non richiede una classe Builder separata — puoi restituire l'oggetto stesso se è mutabile durante l'assemblaggio. Per oggetti immutabili, viene utilizzata una classe Builder annidata simile a 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 ha introdotto @resultBuilder — un meccanismo linguistico per la costruzione dichiarativa di strutture. SwiftUI, AttributedString, SceneBuilder utilizzano result builders. È un'alternativa al Builder classico: invece di una catena di set-metodi, result builder utilizza un blocco di codice con elementi che il compilatore assembla in un array o albero. @ViewBuilder in SwiftUI è l'esempio più famoso: all'interno di body puoi scrivere if, switch, ForEach, e il compilatore costruisce una View dalle condizioni.

Builder vs Telescoping Constructor: confronto degli approcci

Telescoping Constructor — un anti-modello dove una classe ha molti costruttori sovraccaricati con diversi insiemi di parametri. Ad esempio, tre costruttori: HttpConfig(url), HttpConfig(url, timeout), HttpConfig(url, timeout, retries). Con la crescita dei parametri, il numero di costruttori cresce esponenzialmente — per n campi opzionali servono n! combinazioni. Builder risolve questo problema permettendo di specificare solo i campi necessari.

CaratteristicaTelescoping ConstructorBuilderKotlin named args
Quantità di codiceCrescita esponenzialeCrescita lineareMinima
LeggibilitàBassa (qual è il parametro?)Alta (metodo + nome)Alta (nome = valore)
ImmutabilitàImmutableImmutableImmutable
Compatibilità JavaCompletaCompletaNessuna (solo Kotlin)
ValidazioneIn ogni costruttoreIn build() — una voltaIn init()

Kotlin named arguments + valori predefiniti — un'alternativa elegante a Builder in progetti puramente Kotlin. I parametri del costruttore hanno valori predefiniti, il cliente passa solo quelli necessari: HttpConfig(baseUrl = url, timeout = 15_000). Lo svantaggio è l'incapacità di validare i campi obbligatori in fase di compilazione. Builder fornisce campi obbligatori tramite il costruttore Builder (baseUrl è obbligatorio). Per le librerie Java, Builder rimane lo standard de facto.

Builder in Android SDK: AlertDialog, Retrofit, OkHttp

Builder in Android SDK — uno dei modelli più comuni nella libreria standard. 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().

Perché Google usa Builder — retrocompatibilità. Aggiungere un nuovo metodo a Builder non rompe il codice esistente. Se Google usasse un costruttore con 20 parametri, ogni nuovo campo richiederebbe un nuovo sovraccarico. Builder permette di aggiungere set-metodi per anni senza breaking changes. Ad esempio, NotificationCompat.Builder ha aggiunto setBubbleMetadata() in Android 11 senza influenzare il codice esistente.

Builder nelle librerie Kotlin — Ktor (HttpClientBuilder), Coil (ImageRequest.Builder), Room (Room.databaseBuilder(context, AppDatabase.class, "db").fallbackToDestructiveMigration().build()), Navigation (NavOptionsBuilder). Nei progetti Kotlin, Builder è spesso combinato con DSL: Room.databaseBuilder(context, AppDatabase.class, "db").fallbackToDestructiveMigration().build(). Il modello rimane rilevante per le API pubbliche dove retrocompatibilità e interoperabilità Java sono importanti.

Domande frequenti

Quando Builder è eccessivo?

Builder è eccessivo per oggetti con 1-3 campi — un costruttore normale o data class è più chiaro. È anche eccessivo in progetti Kotlin senza interoperabilità Java, dove named arguments + valori predefiniti risolvono lo stesso compito più semplicemente. Builder è giustificato per 5+ campi, validazione complessa o API Java dove named arguments non sono disponibili.

In cosa Builder differisce da Factory?

Builder crea un oggetto complesso passo dopo passo (configurazione dei campi), Factory crea un oggetto intero per tipo o parametri. Builder risponde alla domanda «come assemblare?», Factory risponde «cosa creare?». Builder è spesso combinato con Factory: Factory seleziona il tipo, Builder configura i campi.

Builder è necessario in SwiftUI?

In SwiftUI, il ruolo di Builder è svolto da result builders (@ViewBuilder, @SceneBuilder) e modificatori di View (.font(), .padding()). Il Builder classico non è necessario perché SwiftUI utilizza un approccio dichiarativo e modificatori fluidi. Per i componenti UIKit, Builder è utile: UIAlertController, URLRequest, NSAttributedString.

Come rendere Builder thread-safe?

Builder normalmente non richiede thread safety poiché viene utilizzato in un singolo thread per assemblare un oggetto. Se Builder viene utilizzato in un ambiente multithread (caso raro), sincronizza ogni set-metodo e build(). Alternativa — Immutable Builder: ogni set-metodo restituisce una nuova istanza di Builder con il campo modificato.

Perché Retrofit usa Builder invece di DI?

Retrofit.Builder è un'API pubblica di libreria che deve funzionare senza un contenitore DI. Builder offre flessibilità di configurazione (baseUrl, convertitori, intercettori, adattatori di chiamata personalizzati) senza dipendenze da Dagger o altri framework DI. All'interno di un'applicazione, DI può creare Retrofit una volta tramite Builder, ma Builder stesso rimane parte dell'API pubblica di Retrofit.

Riepilogo

  • Builder — costruzione passo passo di oggetti con interfaccia fluida
  • Kotlin Builder — classico (classe annidata) e DSL (lambda con ricevitore)
  • Swift Builder — classe annidata o @resultBuilder per codice dichiarativo
  • Immutabilità — Builder crea oggetti immutabili tramite costruttore privato
  • Android SDK — AlertDialog, Retrofit, OkHttp, NotificationCompat — standard del settore
  • Retrocompatibilità — aggiungere campi a Builder non rompe il codice esistente
  • Alternativa Kotlin — named arguments + valori predefiniti più semplici per progetti puramente Kotlin

Svilupperemo un'applicazione mobile chiavi in mano

IT Sectr crea applicazioni iOS e Android per startup e aziende dal 2017. Ti consulteremo e ti proporremo la soluzione migliore.

Discuti il progetto

Leggi anche