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 (μέσω lambda με δέκτη). Ο Java-style Builder προτιμάται για συμβατότητα με Android και κατά τη χρήση με κώδικα Java. DSL builder — ιδιωματικός τρόπος Kotlin: η συνάρτηση δέχεται ένα lambda, μέσα στο οποίο το 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 — εναλλακτική χωρίς ένθετη κλάση. Η builder-συνάρτηση δέχεται ένα lambda στο περιβάλλον του αντικειμένου-κατασκευαστή. Αυτό είναι ιδιωματικό για την 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 παραμένει το de facto πρότυπο.
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, μετατροπείς, interceptor, προσαρμοσμένους call adapters) χωρίς εξαρτήσεις από Dagger ή άλλα DI. Μέσα στην εφαρμογή, το DI μπορεί να δημιουργήσει το Retrofit μία φορά μέσω Builder, αλλά ο ίδιος ο Builder παραμένει μέρος του δημόσιου API του Retrofit.
Συμπεράσματα
Θα αναπτύξουμε μια εφαρμογή για κινητά έτοιμη για χρήση
Η IT Sectr δημιουργεί εφαρμογές iOS και Android για νεοφυείς επιχειρήσεις και επιχειρήσεις από το 2017. Θα σας συμβουλεύσουμε και θα προτείνουμε την καλύτερη λύση.
Διαβάστε επίσης