Kotlin: شرح التفويض وتفويض الخصائص في كوتلن

آخر تحديث: 2026-08-26

التفويض هو ميزة كوتلن القاتلة — سطر واحد لتهيئة كسول آمنة للخيوط، سطر واحد لمراقبة تغييرات الخاصية. يستخدم تشارلي تفويض الأصناف لاستبدال كود نمط Decorator المتكرر، وتفويض الخصائص للاستمرار التلقائي لخصائص إعدادات OrderProcessor.

1. ما ستتعلمه


2. قصة مهندس حقيقية

(1) نقطة الألم: كود نمط Decorator المتكرر

استخدم تشارلي نمط Decorator في جافا لإضافة التسجيل والتخزين المؤقت إلى OrderRepository. كل طبقة تطلبت أكثر من 20 سطراً من توابع التفويض — الواجهة بها 8 توابع، و 3 طبقات ديكور تعني 480 سطراً من كود التفويض البحت.

(2) حل تفويض الأصناف في كوتلن

KOTLIN
// جافا: أكثر من 20 سطراً لكل تابع لكل ديكور
class LoggedRepository implements OrderRepository {
    private final OrderRepository delegate;
    @Override public Order findById(String id) { log(); return delegate.findById(id); }
    // ... تكرار لجميع التوابع الثمانية
}

// كوتلن: سطر واحد يفوّض جميع التوابع
class LoggedRepository(
    private val delegate: OrderRepository
) : OrderRepository by delegate  // تفويض تلقائي لجميع التوابع!

سطر واحد يغني عن أكثر من 20 سطراً من تفويض التوابع — تجاوز فقط التوابع التي تريد تحسينها.


3. تفويض الأصناف (by)

(1) الصياغة الأساسية

KOTLIN
interface OrderRepository {
    fun findById(id: String): Order?
    fun save(order: Order)
    fun findAll(): List<Order>
}

class InMemoryRepository : OrderRepository {
    private val storage = mutableMapOf<String, Order>()
    override fun findById(id: String) = storage[id]
    override fun save(order: Order) { storage[order.id] = order }
    override fun findAll() = storage.values.toList()
}

// تفويض الأصناف: تفويض تلقائي لجميع توابع الواجهة
class LoggedRepository(
    private val inner: OrderRepository
) : OrderRepository by inner {  // جميع التوابع مُفوَّضة!
    // تجاوز فقط ما تريد تحسينه
    override fun save(order: Order) {
        println("Saving order: ${order.id}")
        inner.save(order)  // استدعاء المُفوَّض صراحةً
    }
}

(2) التفويض متعدد الطبقات

KOTLIN
// الطبقة 1: تخزين مؤقت
class CachedRepository(
    private val delegate: OrderRepository
) : OrderRepository by delegate {
    private val cache = mutableMapOf<String, Order>()

    override fun findById(id: String): Order? {
        return cache.getOrPut(id) { delegate.findById(id)!! }
    }
}

// الطبقة 2: تسجيل
class LoggedRepository(
    private val delegate: OrderRepository
) : OrderRepository by delegate {
    override fun save(order: Order) {
        println("Saving: ${order.id}")
        delegate.save(order)
    }
}

// تركيب: تسجيل + تخزين مؤقت + في الذاكرة
val repo = LoggedRepository(CachedRepository(InMemoryRepository()))

(3) تفويض الأصناف مقابل الوراثة مقابل Decorator

البُعد الوراثة Decorator (جافا) تفويض الأصناف (كوتلن)
حجم الكود قليل كبير (متكرر) قليل (by)
المرونة وراثة فردية تركيب متعدد الطبقات تركيب متعدد الطبقات
الاقتران عالي منخفض منخفض
وقت التشغيل ربط بصنف الأب ربط بكائن المفوَّض ربط بكائن المفوَّض

4. تفويض الخصائص: by lazy

(1) الاستخدام الأساسي

KOTLIN
class OrderProcessor {
    // lazy: حساب عند أول وصول، آمن للخيوط افتراضياً
    val cache: OrderCache by lazy {
        println("Initializing cache...")
        OrderCache(maxSize = 10_000)
    }

    // lazy مع أمان خيوط مخصص
    val heavyConfig by lazy(LazyThreadSafetyMode.PUBLICATION) {
        loadConfig()  // يمكن استدعاؤها عدة مرات، لكن فقط النتيجة الأولى تُستخدم
    }
}

(2) أوضاع أمان الخيوط في lazy

الوضع السلوك الأداء حالة الاستخدام
SYNCHRONIZED (افتراضي) تهيئة واحدة، قفل مزدوج التحقق أبطأ قليلاً بيئات متعددة الخيوط
PUBLICATION خيوط متعددة قد تحسب، فقط النتيجة الأولى تُستخدم أسرع تهيئة بدون آثار جانبية
NONE بدون مزامنة الأسرع سياق أحادي الخيط معروف

5. تفويض الخصائص: observable / vetoable

(1) observable — إشعار التغيير

KOTLIN
import kotlin.properties.Delegates

class Order(var id: String) {
    // observable: إشعار عند كل تغيير
    var status: String by Delegates.observable("PENDING") { _, old, new ->
        println("Status changed: $old -> $new")
    }
}

val order = Order("ORD-001")
order.status = "CONFIRMED"  // يطبع: Status changed: PENDING -> CONFIRMED
order.status = "SHIPPED"    // يطبع: Status changed: CONFIRMED -> SHIPPED

(2) vetoable — اعتراض التغيير

KOTLIN
class Order(var id: String) {
    // vetoable: يمكن رفض التغيير
    var total: Double by Delegates.vetoable(0.0) { _, old, new ->
        if (new < 0) {
            println("Rejecting negative total: $new")
            false  // نقض: الاحتفاظ بالقيمة القديمة
        } else {
            true  // قبول التغيير
        }
    }
}

val order = Order("ORD-001")
order.total = 299.99  // مقبول
order.total = -50.0   // مرفوض: يطبع تحذيراً، يحتفظ بـ 299.99

(3) مقارنة observable مع vetoable

التفويض السلوك قيمة الإرجاع حالة الاستخدام
observable إشعار بالتغيير، لا اعتراض Unit تسجيل / تدقيق
vetoable يمكن الاعتراض ورفض التغيير Boolean تحقق / قيود

6. تفويض الخصائص المخصص

(1) واجهة ReadWriteProperty

KOTLIN
import kotlin.reflect.KProperty

class PersistentProperty<T>(
    private val key: String,
    private val defaultValue: T
) : ReadWriteProperty<Any?, T> {

    override fun getValue(thisRef: Any?, property: KProperty<*>): T {
        // تحميل من التخزين المستمر (مبسّط)
        return loadFromStore(key) ?: defaultValue
    }

    override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
        // حفظ في التخزين المستمر
        saveToStore(key, value)
    }
}

// تخزين مستمر محاكى
private val store = mutableMapOf<String, Any?>()
private fun <T> loadFromStore(key: String): T? = store[key] as T?
private fun <T> saveToStore(key: String, value: T) { store[key] = value }

(2) استخدام التفويضات المخصصة

KOTLIN
class OrderConfig {
    var maxRetries: Int by PersistentProperty("order.max_retries", 3)
    var timeout: Long by PersistentProperty("order.timeout", 5_000L)
    var taxRate: Double by PersistentProperty("order.tax_rate", 0.08)
}

val config = OrderConfig()
println(config.maxRetries)  // 3 (افتراضي)
config.maxRetries = 5
println(config.maxRetries)  // 5 (مستمر)

(3) مخطط أصناف التفويض

100%
classDiagram
    class ReadOnlyProperty {
        <<interface>>
        +getValue(thisRef, property): T
    }
    class ReadWriteProperty {
        <<interface>>
        +getValue(thisRef, property): T
        +setValue(thisRef, property, value: T)
    }
    class LazyDelegate {
        +getValue(): T
    }
    class ObservableDelegate {
        +setValue()
    }
    class VetoableDelegate {
        +setValue()
    }
    class CustomDelegate {
        +getValue()
        +setValue()
    }
    ReadOnlyProperty <|-- ReadWriteProperty
    ReadOnlyProperty <|-- LazyDelegate
    ReadWriteProperty <|-- ObservableDelegate
    ReadWriteProperty <|-- VetoableDelegate
    ReadWriteProperty <|-- CustomDelegate

7. مثال كامل: بنية تفويض OrderProcessor

KOTLIN
// ============================================
// OrderProcessor - بنية التفويض
// الميزة: تفويض الأصناف + تفويض الخصائص
// ============================================

import kotlin.properties.Delegates
import kotlin.reflect.KProperty

// --- تفويض الأصناف ---

interface OrderRepository {
    fun findById(id: String): Order?
    fun save(order: Order)
    fun findAll(): List<Order>
}

data class Order(val id: String, var total: Double, var status: String, val customer: String)

class InMemoryRepository : OrderRepository {
    private val storage = mutableMapOf<String, Order>()
    override fun findById(id: String) = storage[id]
    override fun save(order: Order) { storage[order.id] = order }
    override fun findAll() = storage.values.toList()
}

class LoggedRepository(private val inner: OrderRepository) : OrderRepository by inner {
    override fun save(order: Order) {
        println("  [LOG] Saving order: ${order.id} (\$${order.total} USD)")
        inner.save(order)
    }
}

class ValidatedRepository(private val inner: OrderRepository) : OrderRepository by inner {
    override fun save(order: Order) {
        require(order.total >= 0) { "Total must be non-negative" }
        require(order.id.startsWith("ORD-")) { "Invalid order ID format" }
        inner.save(order)
    }
}

// --- تفويض الخصائص ---

private val configStore = mutableMapOf<String, Any?>()

class ConfigProperty<T>(private val key: String, private val default: T) {
    operator fun getValue(thisRef: Any?, property: KProperty<*>): T {
        @Suppress("UNCHECKED_CAST")
        return configStore[key] as? T ?: default
    }
    operator fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
        configStore[key] = value
        println("  [CONFIG] $key = $value")
    }
}

class OrderProcessorConfig {
    var maxOrderValue: Double by ConfigProperty("max_order_value", 100_000.0)
    var defaultTaxRate: Double by ConfigProperty("default_tax_rate", 0.08)
    var enableAuditing: Boolean by Delegates.observable(true) { _, _, new ->
        println("  [CONFIG] Auditing ${if (new) "enabled" else "disabled"}")
    }
    var processingTimeout: Long by Delegates.vetoable(30_000L) { _, _, new ->
        if (new < 1_000) { println("  [CONFIG] Rejecting timeout < 1000ms"); false }
        else true
    }
}

fun main() {
    // تركيب: تحقق + تسجيل + في الذاكرة
    val repo: OrderRepository = ValidatedRepository(LoggedRepository(InMemoryRepository()))

    println("=== Saving Orders ===")
    repo.save(Order("ORD-001", 299.99, "CONFIRMED", "Alice"))
    repo.save(Order("ORD-002", 15_000.00, "PENDING", "Bob"))

    println("\n=== Find Order ===")
    println(repo.findById("ORD-001"))

    // تفويض الخصائص
    println("\n=== Configuration ===")
    val config = OrderProcessorConfig()
    println("Max order value: \$${config.maxOrderValue} USD")
    config.defaultTaxRate = 0.10
    config.enableAuditing = false
    config.processingTimeout = 500  // مرفوض
    config.processingTimeout = 60_000  // مقبول
    println("Timeout: ${config.processingTimeout}ms")
}

المخرجات:

TEXT 📖 للعرض فقط
=== Saving Orders ===
  [LOG] Saving order: ORD-001 ($299.99 USD)
  [LOG] Saving order: ORD-002 ($15000.0 USD)

=== Find Order ===
Order(id=ORD-001, total=299.99, status=CONFIRMED, customer=Alice)

=== Configuration ===
Max order value: $100000.0 USD
  [CONFIG] default_tax_rate = 0.1
  [CONFIG] Auditing disabled
  [CONFIG] Rejecting timeout < 1000ms
  [CONFIG] processingTimeout = 60000
Timeout: 60000ms

8. أمثلة عملية سريعة

▶ مثال: class delegation

KOTLIN
// واجهة
interface Repository {
    fun findAll(): List<String>
    fun findById(id: String): String?
    fun save(item: String)
}

// تنفيذ بسيط
class InMemoryRepository : Repository {
    private val data = mutableMapOf<String, String>()

    override fun findAll(): List<String> = data.values.toList()
    override fun findById(id: String): String? = data[id]
    override fun save(item: String) {
        data[item] = item
        println("Saved: $item")
    }
}

// مع class delegation - يفوّض كل شيء لـ inner
class LoggingRepository(private val inner: Repository) : Repository by inner {
    override fun save(item: String) {
        println("[LOG] Saving $item...")
        inner.save(item)
        println("[LOG] Saved $item")
    }
}

val repo = LoggingRepository(InMemoryRepository())
repo.save("item-1")
println("All: ${repo.findAll()}")
println("Get item-1: ${repo.findById("item-1")}")

**المخرجات:

TEXT 📖 للعرض فقط
[LOG] Saving item-1...
Saved: item-1
[LOG] Saved item-1
All: [item-1]
Get item-1: item-1

▶ مثال: by lazy

KOTLIN
class ExpensiveResource {
    val data: String by lazy {
        println("Loading expensive data...")
        Thread.sleep(1000)  // محاكاة عملية ثقيلة
        "Loaded data"
    }

    val timestamp: Long by lazy(LazyThreadSafetyMode.SYNCHRONIZED) {
        println("Generating timestamp...")
        System.currentTimeMillis()
    }

    val randomValue: Int by lazy(LazyThreadSafetyMode.NONE) {
        // غير آمن للخيوط، لكن أسرع
        println("Computing random...")
        (1..100).random()
    }
}

val resource = ExpensiveResource()
println("--- Created ---")
println("Data: ${resource.data}")  // يتم التحميل هنا
println("Data again: ${resource.data}")  // مُخزن مؤقتاً
println("Time: ${resource.timestamp}")
println("Random: ${resource.randomValue}")

**المخرجات:

TEXT 📖 للعرض فقط
--- Created ---
Loading expensive data...
Data: Loaded data
Data again: Loaded data
Generating timestamp...
Time: 1700000000000
Computing random...
Random: 42

▶ مثال: by observable (من java.beans)

KOTLIN
import java.beans.PropertyChangeSupport
import java.beans.PropertyChangeListener

class Account(initialBalance: Double) {
    private val changes = PropertyChangeSupport(this)

    var balance: Double = initialBalance
        set(value) {
            val old = field
            field = value
            changes.firePropertyChange("balance", old, value)
        }

    fun addObserver(listener: PropertyChangeListener) {
        changes.addPropertyChangeListener(listener)
    }
}

val account = Account(1000.0)
account.addObserver { event ->
    println("Property '${event.propertyName}' changed from ${event.oldValue} to ${event.newValue}")
}

println("Initial: ${account.balance}")
account.balance = 1500.0
account.balance = 2000.0
account.balance = 750.0

**المخرجات:

TEXT 📖 للعرض فقط
Initial: 1000.0
Property 'balance' changed from 1000.0 to 1500.0
Property 'balance' changed from 1500.0 to 2000.0
Property 'balance' changed from 2000.0 to 750.0

▶ مثال: by vetoable

KOTLIN
import kotlin.properties.Delegates

class VoteBox {
    var count: Int by Delegates.vetoable(0) { _, old, new ->
        // الرفض إذا كانت القيمة الجديدة أقل
        new >= old
    }

    fun vote(forCandidate: Int) {
        val old = count
        count += forCandidate
        println("Vote: +$forCandidate, count: $old -> $count")
    }
}

val box = VoteBox()
box.vote(5)
box.vote(3)
println("Total: ${box.count}")

// محاولة التصويت بقيمة سالبة - سيتم الرفض
box.vote(-2)
println("After rejected: ${box.count}")

**المخرجات:

TEXT 📖 للعرض فقط
Vote: +5, count: 0 -> 5
Vote: +3, count: 5 -> 8
Total: 8
Vote: -2, count: 8 -> 8
After rejected: 8

▶ مثال: by map (التسلسل لـ Map)

KOTLIN
// التسلسل لـ Map - مفيد لتحليل JSON
class User private constructor(
    private val map: Map<String, Any?>
) {
    val name: String by map
    val age: Int by map
    val email: String? by map

    constructor(name: String, age: Int, email: String?) : this(
        mapOf("name" to name, "age" to age, "email" to email)
    )

    fun toMap(): Map<String, Any?> = map

    override fun toString(): String = "User(name=$name, age=$age, email=$email)"
}

val alice = User("Alice", 30, "alice@example.com")
println(alice)
println("Name via delegation: ${alice.name}")

// التسلسل لـ Map من JSON-like input
val user2 = User(mapOf(
    "name" to "Bob",
    "age" to 25,
    "email" to null
) as Map<String, Any?>)
println(user2)

**المخرجات:

TEXT 📖 للعرض فقط
User(name=Alice, age=Alice, age=30, email=alice@example.com)
User(name=Alice, age=30, email=alice@example.com)
Name via delegation: Alice
User(name=Bob, age=25, email=null)

▶ مثال: Delegates.notNull و observable

KOTLIN
import kotlin.properties.Delegates

class Settings {
    // notNull - يجب التعيين قبل القراءة
    var apiKey: String by Delegates.notNull()

    // observable مع onChange callback
    var debug: Boolean by Delegates.observable(false) { _, old, new ->
        println("Debug mode: $old -> $new")
        if (new) println("Logging enabled")
    }
}

val settings = Settings()
try {
    println(settings.apiKey)  // يلقي IllegalStateException
} catch (e: Exception) {
    println("Error: ${e.message}")
}

settings.apiKey = "secret-key-123"
println("API key: ${settings.apiKey}")

settings.debug = true
settings.debug = false
settings.debug = true

**المخرجات:

TEXT 📖 للعرض فقط
Error: Property apiKey should be initialized before get.
API key: secret-key-123
Debug mode: false -> true
Logging enabled
Debug mode: true -> false
Debug mode: false -> true
Logging enabled

▶ مثال: Local delegated properties

KOTLIN
// تأخير قيمة حسابية
fun processData(data: List<Int>): Int {
    val sum: Int by lazy {
        println("Computing sum...")
        data.sum()
    }

    val avg: Double by lazy {
        println("Computing average...")
        sum.toDouble() / data.size
    }

    val max: Int by lazy {
        println("Computing max...")
        data.maxOrNull() ?: 0
    }

    println("Sum: $sum")      // يتم الحساب هنا
    println("Sum again: $sum")  // مخزن
    println("Average: $avg")    // يتم الحساب هنا
    println("Max: $max")        // يتم الحساب هنا

    return sum
}

println(processData(listOf(1, 2, 3, 4, 5)))

**المخرجات:

TEXT 📖 للعرض فقط
Computing sum...
Sum: 15
Sum again: 15
Computing average...
Average: 3.0
Computing max...
Max: 5
15

❓ أسئلة شائعة

س ما الفرق بين تفويض الأصناف والوراثة؟
ج تفويض الأصناف هو تركيب (has-a)؛ الوراثة هي تصنيف فرعي (is-a). التفويض أكثر مرونة — يمكنك استبدال المفوَّض وقت التشغيل وتجنب الاقتران المحكم للوراثة.
س هل تهيئة by lazy تحدث دائماً؟
ج ليس بالضرورة. إذا لم تُنصل الخاصية أبداً، لا تنفذ التهيئة أبداً. هذه هي الميزة الأساسية لـ lazy — تجنب حمل التهيئة غير الضروري.
س ما أداء تفويض الخصائص؟
ج كل قراءة/كتابة للخاصية تتحمل استدعاء دالة إضافية (getValue/setValue)، لكن تحسين التضمين في JVM يزيل عادةً هذا الحمل. للمسارات الساخنة، قيّس قبل اتخاذ القرار.
س هل يمكن لصنف استخدام تفويضات by متعددة؟
ج تفويض الأصناف يفوّض واجهة واحدة (وراثة متعددة عبر واجهات متعددة)، وتفويض الخصائص يفوّض كل خاصية بشكل مستقل.
س هل يمكن استخدام observable للبرمجة التفاعلية؟
ج نطاقها محدود — تُشعر فقط داخل نفس الكائن. للتفاعلية عبر المكونات، استخدم StateFlow/SharedFlow. observable أنسب لسجلات التدقيق والإشعارات البسيطة.
س هل يجب أن تطبق التفويضات المخصصة ReadWriteProperty؟
ج غير مطلوب. تحتاج فقط لتوفير getValuesetValue لـ var). ReadWriteProperty هي مجرد واجهة ملائمة.

📖 ملخص


📝 تمارين

  1. مبتدئ (⭐): استخدم by lazy لتنفيذ اتصال قاعدة بيانات بتهيئة كسولة يطبع "Connecting..." عند أول وصول. تلميح: val connection by lazy { ... }
  2. متوسط (⭐⭐): استخدم تفويض الأصناف لتنفيذ LoggedOrderRepo، مع تجاوز تابع save فقط لإضافة التسجيل. تلميح: class LoggedOrderRepo(val inner: OrderRepository) : OrderRepository by inner { ... }
  3. متقدم (⭐⭐⭐): نفّذ تفويض خاصية مخصص TraceDelegate يطبع اسم الخاصية وقيمتها عند كل قراءة وكتابة. تلميح: دوال getValue + setValue العاملة

← السابق | التالي →

Web-Tutorial.com

فريق Web-Tutorial التقني

منصة دروس برمجية يديرها عدة مطورين. كل درس يتم كتابته ومراجعته بواسطة مطورين متخصصين في المجال. نعمل على ضمان دقة وموثوقية المحتوى — إذا لاحظت أي مشكلة، فيرجى إخبارنا.

100%