Builder — أساسيات نمط البناء في تطوير التطبيقات المحمولة

المؤلف: IT Sectr نُشر: 2026-02-17 وقت القراءة: 7 دق

Builder — نمط إنشائي يسمح بإنشاء كائنات معقدة خطوة بخطوة. على عكس المنشئ بعشرات المعاملات، يقوم Builder بتجميع الكائن عبر سلسلة من الاستدعاءات، كل منها يهيئ حقلاً واحداً. النمط مفيد بشكل خاص للكائنات ذات المعاملات الاختيارية المتعددة: تهيئة عميل الشبكة، إعدادات قاعدة البيانات، بناة التنبيهات والتنقل. للمزيد على Refactoring Guru: Builder.

النقاط الرئيسية

  • Builder — بناء الكائنات خطوة بخطوة مع فصل العملية عن النتيجة
  • Fluent interface — سلسلة استدعاءات set()/with() للتهيئة المريحة
  • الثبات — Builder ينشئ كائناً جاهزاً لا يتطلب setters
  • التوافق العكسي — إضافة حقول جديدة إلى Builder دون كسر العملاء
  • Kotlin DSL vs Builder — Kotlin تقدم type-safe builders كبديل

ما هو Builder: جوهر نمط البناء؟

Builder — نمط إنشائي من GoF يفصل بناء كائن معقد عن تمثيله. يمكن لنفس عملية البناء إنشاء تمثيلات مختلفة. Builder مفيد عندما يكون للكائن العديد من المعاملات الاختيارية ويكون المنشئ بعشرة حقول غير قابل للقراءة وغير مرن. يحل النمط أيضاً مشكلة Telescoping Constructor — النمط المعاكس حيث ينمو عدد تحميلات المنشئ بشكل أسي.

هيكل Builder يتضمن فئة داخلية ثابتة Builder بحقول تعكس حقول الفئة الرئيسية. كل set-طريقة تعيد Builder (this) للربط المتدفق. الطريقة النهائية 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

Builder في Kotlin له نهجان: Builder كلاسيكي على نمط Java (عبر فئة متداخلة) وBuilder بنمط DSL في Kotlin (عبر لامدا مع مستقبل). Builder بنمط Java مفضل للتوافق مع 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-fields. تستخدم DSL builders بنشاط في Ktor Client, kotlinx.serialization, Compose (Modifier). DSL builder غير متوافق مع Java وغير مناسب للمكتبات ذات واجهة Java.

Builder في Swift: result builders والسلاسل

Builder في Swift — Swift ليس لديها نمط Builder مدمج، لكن الواجهة المتدفقة تُنفَّذ بسهولة عبر دوال تعيد Self. كل دالة تهيئ خاصية وتعيد self. على عكس Kotlin، Swift لا تتطلب فئة Builder منفصلة — يمكن إرجاع الكائن نفسه إذا كان قابلاً للتغيير أثناء التجميع. للكائنات الثابتة، تُستخدم فئة 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 مقابل Telescoping Constructor: مقارنة المناهج

Telescoping Constructor — نمط معاكس حيث تحتوي الفئة على العديد من المنشئات المحملة بشكل زائد بمجموعات مختلفة من المعاملات. على سبيل المثال، ثلاثة منشئات: HttpConfig(url), HttpConfig(url, timeout), HttpConfig(url, timeout, retries). مع زيادة المعاملات، ينمو عدد المنشئات بشكل أسي — لـ n حقلاً اختيارياً تحتاج n! تركيبة. Builder يحل هذه المشكلة بالسماح بتحديد الحقول المطلوبة فقط.

الخاصيةTelescoping ConstructorBuilderKotlin named args
كمية الكودنمو أسينمو خطيأدنى
قابلية القراءةمنخفضة (أي معامل هو أي؟)عالية (دالة + اسم)عالية (اسم = قيمة)
الثباتثابتثابتثابت
التوافق مع Javaكاملكامللا شيء (Kotlin فقط)
التحققفي كل منشئفي build() — مرة واحدةفي init()

Kotlin named arguments + قيم افتراضية — بديل أنيق لـ 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-دوال لسنوات دون تغييرات مكسرة. على سبيل المثال، NotificationCompat.Builder أضاف setBubbleMetadata() في Android 11 دون التأثير على الكود الموجود.

Builder في مكتبات Kotlin — Ktor (HttpClientBuilder), Coil (ImageRequest.Builder), Room (Room.databaseBuilder(context, AppDatabase.class, "db").fallbackToDestructiveMigration().build()), Navigation (NavOptionsBuilder). في مشاريع Kotlin، غالباً ما يُدمج Builder مع DSL: Room.databaseBuilder(context, AppDatabase.class, "db").fallbackToDestructiveMigration().build(). يبقى النمط ملائماً لواجهات البرمجة العامة حيث يهم التوافق العكسي والتشغيل البيني مع Java.

الأسئلة الشائعة

متى يكون Builder مفرطاً؟

Builder مفرط للكائنات ذات 1-3 حقول — المنشئ العادي أو data class أوضح. كما أنه مفرط في مشاريع Kotlin دون تشغيل بيني مع Java، حيث named arguments + القيم الافتراضية تحل المهمة نفسها بشكل أبسط. Builder مبرر لـ 5+ حقول، أو تحقق معقد، أو واجهات Java حيث 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 تستخدم نهجاً تصريحياً ومعدِّلات متدفقة. لمكونات UIKit، Builder مفيد: UIAlertController, URLRequest, NSAttributedString.

كيف نجعل Builder آمناً للخيوط؟

Builder عادة لا يتطلب أمان الخيوط لأنه يُستخدم في خيط واحد لتجميع الكائن. إذا استخدم Builder في بيئة متعددة الخيوط (حالة نادرة)، قم بمزامنة كل set-دالة وbuild(). البديل — Immutable Builder: كل set-دالة تعيد نسخة جديدة من Builder مع الحقل المعدَّل.

لماذا يستخدم Retrofit Builder بدلاً من DI؟

Retrofit.Builder هي واجهة برمجة عامة للمكتبة يجب أن تعمل دون حاوية DI. Builder يوفر مرونة التهيئة (baseUrl, المحولات, المعترضون, محولات الاستدعاء المخصصة) دون اعتماديات على Dagger أو أطر DI أخرى. داخل التطبيق، يمكن لـ DI إنشاء Retrofit مرة واحدة عبر Builder، لكن Builder نفسه يبقى جزءاً من واجهة Retrofit العامة.

الخلاصة

  • Builder — بناء الكائنات خطوة بخطوة بواجهة متدفقة
  • Kotlin Builder — كلاسيكي (فئة متداخلة) وDSL (لامدا مع مستقبل)
  • Swift Builder — فئة متداخلة أو @resultBuilder لكود تصريحي
  • الثبات — Builder ينشئ كائنات ثابتة عبر منشئ خاص
  • Android SDK — AlertDialog, Retrofit, OkHttp, NotificationCompat — معيار الصناعة
  • التوافق العكسي — إضافة حقول إلى Builder لا يكسر الكود الموجود
  • بديل Kotlin — named arguments + قيم افتراضية أبسط لمشاريع Kotlin الخالصة

سنقوم بتطوير تطبيق جوال جاهز

تقدم IT Sectr تطبيقات iOS وAndroid للشركات الناشئة والشركات منذ عام 2017. سوف نقدم لك النصح ونقترح أفضل حل.

مناقشة المشروع

اقرأ أيضًا