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 استفاده کنیم — اشیاء با ۵+ فیلد که تنها ۲-۳ تای آنها اجباری است. اشیاء پیکربندی (RequestConfig, DatabaseConfig). اشیاء با منطق اعتبارسنجی پیچیده در زمان ایجاد. اشیایی که پس از ایجاد باید تغییرناپذیر (immutable) باشند. در Android از Builder به طور فعال در SDK استفاده میشود: AlertDialog.Builder, Retrofit.Builder, OkHttpClient.Builder, NotificationCompat.Builder.
Kotlin Builder دو رویکرد دارد: Builder کلاسیک به سبک Java (از طریق کلاس تو در تو) و DSL builder به سبک Kotlin (از طریق لامبدا با دریافتکننده). Builder به سبک Java برای سازگاری با 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 از سازنده با ۲۰ پارامتر استفاده میکرد، هر فیلد جدید نیاز به یک بارگذاری اضافی جدید داشت. Builder اجازه میدهد سالها بدون breaking changes متدهای set اضافه شوند. مثلاً 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 برای اشیاء با ۱-۳ فیلد اضافی است — سازنده معمولی یا data class قابلفهمتر است. همچنین در پروژههای Kotlin بدون قابلیت همکاری با Java اضافی است، جایی که named arguments + default values کار مشابه را سادهتر انجام میدهند. Builder برای ۵+ فیلد، اعتبارسنجی پیچیده یا 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, مبدلها, interceptors, آداپتورهای تماس سفارشی) را بدون وابستگی به Dagger یا سایر DI فراهم میکند. در داخل برنامه، DI میتواند یک بار Retrofit را از طریق Builder ایجاد کند، اما خود Builder بخشی از API عمومی Retrofit باقی میماند.
جمعبندی
ما یک اپلیکیشن موبایل به صورت کلید در دست توسعه خواهیم داد
IT Sectr از سال 2017 برنامههای iOS و Android را برای استارتاپها و کسبوکارها ایجاد میکند. ما به شما مشاوره میدهیم و بهترین راهحل را پیشنهاد خواهیم کرد.
همچنین بخوانید