Kotlin: شرح التفويض وتفويض الخصائص في كوتلن
آخر تحديث: 2026-08-26
التفويض هو ميزة كوتلن القاتلة — سطر واحد لتهيئة كسول آمنة للخيوط، سطر واحد لمراقبة تغييرات الخاصية. يستخدم تشارلي تفويض الأصناف لاستبدال كود نمط Decorator المتكرر، وتفويض الخصائص للاستمرار التلقائي لخصائص إعدادات OrderProcessor.
1. ما ستتعلمه
- تفويض الأصناف: نمط Decorator بدون كود متكرر
by lazy: حساب كسول آمن للخيوطDelegates.observable/Delegates.vetoable: مراقبة تغييرات الخاصية واعتراضها- تفويض الخصائص المخصص:
ReadWriteProperty/getValue/setValue - تشارلي في العمل:
ValidatedRepository+ConfigPropertyتفويض مخصص
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) مخطط أصناف التفويض
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؟ج غير مطلوب. تحتاج فقط لتوفير
getValue (و setValue لـ var). ReadWriteProperty هي مجرد واجهة ملائمة.📖 ملخص
- تفويض الأصناف بـ
byيفوّض تلقائياً جميع توابع الواجهة — صفر كود متكرر لنمط Decorator by lazyيوفر تهيئة كسول آمنة للخيوط، مثالي لإنشاء الكائنات الثقيلة عند الطلبDelegates.observableيراقب تغييرات الخاصية؛Delegates.vetoableيمكنه الاعتراض ورفضها- تفويضات الخصائص المخصصة تحتاج فقط دوال
getValue/setValueالعاملة - فضّل التفويض على الوراثة — التركيب أكثر مرونة من التصنيف الفرعي
- تركيب التفويض متعدد الطبقات يحقق فصل الاهتمامات: تسجيل + تخزين مؤقت + تحقق
📝 تمارين
- مبتدئ (⭐): استخدم
by lazyلتنفيذ اتصال قاعدة بيانات بتهيئة كسولة يطبع "Connecting..." عند أول وصول. تلميح:val connection by lazy { ... } - متوسط (⭐⭐): استخدم تفويض الأصناف لتنفيذ
LoggedOrderRepo، مع تجاوز تابعsaveفقط لإضافة التسجيل. تلميح:class LoggedOrderRepo(val inner: OrderRepository) : OrderRepository by inner { ... } - متقدم (⭐⭐⭐): نفّذ تفويض خاصية مخصص
TraceDelegateيطبع اسم الخاصية وقيمتها عند كل قراءة وكتابة. تلميح: دوالgetValue+setValueالعاملة