Kotlin: شرح بناء DSL في كوتلن
آخر تحديث: 2026-08-26
DSL هي أكثر ميزات كوتلن تعبيراً — يُقرأ كملف إعدادات لكنه فعلياً كود كوتلن آمن الأنواع. يستخدمه تشارلي لبناء DSL لإعدادات الطلبات، مما يتيح للمستخدمين غير التقنيين تعريف قواعد الطلبات.
1. ما ستتعلمه
- عناصر تصميم DSL: مستقبلات لامبدا
Type.() -> Unit - البنّاؤون الآمنون الأنواع:
apply/run/with @DslMarker: منع تسريب النطاق- Gradle Kotlin DSL كمثال واقعي
- تشارلي في العمل:
OrderBuilder+OrderItemBuilderDSL متداخل
2. قصة مهندس حقيقية
(1) نقطة الألم: صيغات إعدادات غير آمنة
استخدمت قواعد طلبات تشارلي إعدادات YAML، لكن YAML لا يمتلك فحص أنواع — الأخطاء الإملائية والحقول المفقودة تظهر فقط وقت التشغيل. هذا تسبب في 2-3 حوادث إنتاج شهرياً من أخطاء الإعدادات.
(2) حل DSL في كوتلن
KOTLIN
// YAML: لا أمان أنواع، أخطاء وقت التشغيل
order:
customer: Alice
items:
- sku: ABC
qty: three // خطأ إملائي! يجب أن يكون 3
// DSL كوتلن: آمن الأنواع، أخطاء وقت الترجمة
order {
customer = "Alice"
items {
item { sku = "ABC"; qty = 3 } // فحص أنواع وقت الترجمة!
}
}
DSL كوتلن = مرونة الإعدادات + أمان الأنواع في الكود. المترجم يلتقط أخطاء الإعدادات وقت الترجمة.
3. مستقبلات لامبدا
(1) لامبدا مع مستقبل
KOTLIN
// لامبدا عادية: معامل
val greet: (String) -> Unit = { name -> println("Hello, $name") }
// لامبدا مع مستقبل: 'this' يشير للمستقبل
val greet2: String.() -> Unit = { println("Hello, $this") }
// الاستخدام
greet2("Alice") // Hello, Alice
"Alice".greet2() // Hello, Alice (استدعاء شبيه بالامتداد)
(2) with / apply / run
KOTLIN
val order = Order()
// with: تنفيذ كتلة مع مستقبل، إرجاع نتيجة الكتلة
val summary = with(order) {
id = "ORD-001"
total = 299.99
"Order $id created" // قيمة الإرجاع
}
// apply: تنفيذ كتلة مع مستقبل، إرجاع المستقبل نفسه
val configured = order.apply {
id = "ORD-001" // 'this' هو order
total = 299.99
} // يرجع order
// run: تنفيذ كتلة مع مستقبل، إرجاع نتيجة الكتلة
val result = order.run {
id = "ORD-002"
"Processed $id"
}
(3) مقارنة دوال النطاق
| الدالة | المرجع | قيمة الإرجاع | حالة الاستخدام |
|---|---|---|---|
apply |
this |
المستقبل | إعداد كائن (تسلسل) |
run |
this |
نتيجة الكتلة | تنفيذ + إرجاع نتيجة |
with |
this |
نتيجة الكتلة | استخدام بدون امتداد |
let |
it |
نتيجة الكتلة | تحويلات أمان البطلان |
also |
it |
المستقبل | آثار جانبية (تسجيل / تحقق) |
4. البنّاؤون الآمنون الأنواع
(1) بنّاء أساسي
KOTLIN
class OrderBuilder {
var id: String = ""
var total: Double = 0.0
var status: String = "PENDING"
var customer: String = ""
private val items = mutableListOf<OrderItem>()
fun item(block: OrderItemBuilder.() -> Unit) {
items.add(OrderItemBuilder().apply(block).build())
}
fun build() = Order(id, total, status, customer, items.toList())
}
class OrderItemBuilder {
var sku: String = ""
var qty: Int = 1
var unitPrice: Double = 0.0
fun build() = OrderItem(sku, qty, unitPrice)
}
// نقطة دخول DSL
fun order(block: OrderBuilder.() -> Unit): Order {
return OrderBuilder().apply(block).build()
}
(2) استخدام بنّاء DSL
KOTLIN
val myOrder = order {
id = "ORD-001"
total = 299.99
customer = "Alice"
item {
sku = "SKU-WIDGET"
qty = 3
unitPrice = 9.99
}
item {
sku = "SKU-GADGET"
qty = 1
unitPrice = 149.99
}
}
5. @DslMarker لمنع تسريب النطاق
(1) المشكلة: غموض المستقبل الضمني
KOTLIN
order {
id = "ORD-001"
item {
sku = "ABC"
// خطر: 'id' هنا يمكن أن يشير إلى OrderBuilder.id الخارجي!
id = "ITEM-001" // أي id؟ الطلب أم العنصر؟
}
}
(2) حل @DslMarker
KOTLIN
@DslMarker
annotation class OrderDsl
@OrderDsl
class OrderBuilder {
var id: String = ""
fun item(block: OrderItemBuilder.() -> Unit) { ... }
}
@OrderDsl
class OrderItemBuilder {
var sku: String = ""
// الآن: لا يمكن الوصول إلى OrderBuilder.id الخارجي من هنا!
}
order {
id = "ORD-001" // صحيح: OrderBuilder.id
item {
sku = "ABC" // صحيح: OrderItemBuilder.sku
// id = "X" // خطأ: لا يمكن الوصول للنطاق الخارجي!
}
}
(3) مخطط بناء DSL
flowchart TD
A[كتلة order] --> B[OrderBuilder<br/>id, total, customer]
B --> C[كتلة item]
C --> D[OrderItemBuilder<br/>sku, qty, price]
D --> E[بناء OrderItem]
E --> F[إضافة لقائمة items]
B --> G[بناء Order]
G --> H[إرجاع كائن Order]
6. مقارنة أنماط تصميم DSL
| النمط | التنفيذ | المقروئية | أمان الأنواع | حالة الاستخدام |
|---|---|---|---|---|
| بنّاء + مستقبل لامبدا | fun order(block: Builder.() -> Unit) |
★★★★★ | ✅ وقت الترجمة | إعدادات متداخلة معقدة |
| دوال infix | infix fun A.to(b: B) |
★★★★ | ✅ وقت الترجمة | تسلسل بسيط |
| تحميل العوامل الزائد | operator fun plus() |
★★★ | ✅ وقت الترجمة | عمليات رياضية / مجموعات |
| معالجة التعليقات | @Annotation class X |
★★★ | ✅ وقت التوليد | تقليل الكود المتكرر |
| وكيل ديناميكي | أسلوب dynamic |
★★ | ❌ وقت التشغيل | إعدادات مرنة / نصوص برمجية |
7. مثال Gradle Kotlin DSL
أحد أنجح تطبيقات DSL في كوتلن — نصوص بناء Gradle:
KOTLIN
// build.gradle.kts - هذا IS لغة DSL كوتلن!
plugins {
kotlin("jvm") version "1.9.22"
application
}
dependencies {
implementation(kotlin("stdlib"))
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3")
testImplementation(kotlin("test"))
}
kotlin {
jvmToolchain(17)
}
application {
mainClass.set("com.order.MainKt")
}
7. مثال كامل: DSL إعدادات طلبات OrderProcessor
KOTLIN
// ============================================
// OrderProcessor - DSL إعدادات الطلبات
// الميزة: بنّاء طلبات آمن الأنواع بـ DSL
// ============================================
@DslMarker
annotation class OrderDsl
data class OrderItem(val sku: String, val qty: Int, val unitPrice: Double) {
val subtotal: Double get() = qty * unitPrice
}
data class Address(val street: String, val city: String, val country: String)
data class Order(
val id: String,
val customer: String,
val items: List<OrderItem>,
val shippingAddress: Address?,
val priority: String,
val notes: String?
) {
val total: Double get() = items.sumOf { it.subtotal }
}
@OrderDsl
class OrderBuilder {
var id: String = ""
var customer: String = ""
var priority: String = "STANDARD"
var notes: String? = null
private val items = mutableListOf<OrderItem>()
private var shippingAddress: Address? = null
fun item(block: OrderItemBuilder.() -> Unit) {
items.add(OrderItemBuilder().apply(block).build())
}
fun items(block: ItemsBuilder.() -> Unit) {
items.addAll(ItemsBuilder().apply(block).build())
}
fun shipTo(block: AddressBuilder.() -> Unit) {
shippingAddress = AddressBuilder().apply(block).build()
}
fun build(): Order {
require(id.isNotBlank()) { "Order ID is required" }
require(customer.isNotBlank()) { "Customer is required" }
return Order(id, customer, items.toList(), shippingAddress, priority, notes)
}
}
@OrderDsl
class OrderItemBuilder {
var sku: String = ""
var qty: Int = 1
var unitPrice: Double = 0.0
fun build(): OrderItem {
require(sku.isNotBlank()) { "SKU is required" }
return OrderItem(sku, qty, unitPrice)
}
}
@OrderDsl
class ItemsBuilder {
private val items = mutableListOf<OrderItem>()
fun item(block: OrderItemBuilder.() -> Unit) {
items.add(OrderItemBuilder().apply(block).build())
}
fun build() = items.toList()
}
@OrderDsl
class AddressBuilder {
var street: String = ""
var city: String = ""
var country: String = ""
fun build() = Address(street, city, country)
}
// نقطة دخول DSL
fun order(block: OrderBuilder.() -> Unit): Order =
OrderBuilder().apply(block).build()
fun main() {
// DSL آمن الأنواع: يُقرأ كإعدادات، تتحقق منه المترجم
val myOrder = order {
id = "ORD-001"
customer = "Alice"
priority = "HIGH"
items {
item { sku = "SKU-WIDGET"; qty = 3; unitPrice = 9.99 }
item { sku = "SKU-GADGET"; qty = 1; unitPrice = 149.99 }
item { sku = "SKU-DOOHICKEY"; qty = 5; unitPrice = 4.50 }
}
shipTo {
street = "123 Main St"
city = "New York"
country = "US"
}
notes = "Rush delivery requested"
}
println("=== Order Summary ===")
println("ID: ${myOrder.id}")
println("Customer: ${myOrder.customer}")
println("Priority: ${myOrder.priority}")
println("\nItems:")
myOrder.items.forEach { item ->
println(" ${item.sku} x${item.qty} @ \$${item.unitPrice} = \$${item.subtotal} USD")
}
println("\nTotal: \$${myOrder.total} USD")
println("Ship to: ${myOrder.shippingAddress}")
println("Notes: ${myOrder.notes}")
// تحقق: أمان أنواع وقت الترجمة
// sku = 123 // خطأ: متوقع String، وليس Int
// qty = "three" // خطأ: متوقع Int، وليس String
}
المخرجات:
TEXT
📖 للعرض فقط
=== Order Summary ===
ID: ORD-001
Customer: Alice
Priority: HIGH
Items:
SKU-WIDGET x3 @ $9.99 = $29.97 USD
SKU-GADGET x1 @ $149.99 = $149.99 USD
SKU-DOOHICKEY x5 @ $4.5 = $22.5 USD
Total: $202.46 USD
Ship to: Address(street=123 Main St, city=New York, country=US)
Notes: Rush delivery requested
9. أمثلة عملية سريعة
▶ مثال: DSL بسيط باستخدام lambdas
KOTLIN
// بناء HTML بسيط باستخدام DSL
class Tag(val name: String) {
private val children = mutableListOf<String>()
fun content(text: String) {
children.add(text)
}
fun render(): String {
val inner = children.joinToString("")
return "<$name>$inner</$name>"
}
}
class HtmlBuilder {
private val root = Tag("html")
fun body(block: Tag.() -> Unit) {
val body = Tag("body")
body.block()
root.content(body.render())
}
fun head(block: Tag.() -> Unit) {
val head = Tag("head")
head.block()
root.content(head.render())
}
fun render(): String = root.render()
}
// DSL functions
fun html(block: HtmlBuilder.() -> Unit): String {
val builder = HtmlBuilder()
builder.block()
return builder.render()
}
fun Tag.h1(block: Tag.() -> Unit) {
val h1 = Tag("h1")
h1.block()
content(h1.render())
}
fun Tag.p(block: Tag.() -> Unit) {
val p = Tag("p")
p.block()
content(p.render())
}
// الاستخدام
val page = html {
head {
content("<title>My Page</title>")
}
body {
h1 {
content("مرحبا بك في Kotlin DSL")
}
p {
content("هذا مثال على DSL باستخدام lambdas")
}
}
}
println(page)
**المخرجات:
TEXT
📖 للعرض فقط
<html><head><title>My Page</title></head><body><h1>مرحبا بك في Kotlin DSL</h1><p>هذا مثال على DSL باستخدام lambdas</p></body></html>
▶ مثال: DSL مع @DslMarker
KOTLIN
// @DslMarker يمنع الوصول العرضي للنطاقات الخارجية
@DslMarker
annotation class HtmlTag
@DslMarker
annotation class BodyTag
// كل نطاق له marker خاص به
@HtmlTag
class SafeHtml {
private val content = StringBuilder()
@BodyTag
fun body(block: Body.() -> Unit) {
val b = Body()
b.block()
content.append(b.render())
}
fun render() = "<html>${content}</html>"
}
@BodyTag
class Body {
private val content = StringBuilder()
@BodyTag
fun div(block: Div.() -> Unit) {
val d = Div()
d.block()
content.append(d.render())
}
fun text(t: String) { content.append(t) }
fun render() = "<body>${content}</body>"
}
@BodyTag
class Div {
private val content = StringBuilder()
@BodyTag
fun span(block: Span.() -> Unit) {
val s = Span()
s.block()
content.append(s.render())
}
fun text(t: String) { content.append(t) }
fun render() = "<div>${content}</div>"
}
@BodyTag
class Span {
private val content = StringBuilder()
fun text(t: String) { content.append(t) }
fun render() = "<span>${content}</span>"
}
// دوال DSL
fun html(block: SafeHtml.() -> Unit): String {
val h = SafeHtml()
h.block()
return h.render()
}
@BodyTag
fun Body.div(block: Div.() -> Unit) {} // نوع مختلف
@BodyTag
fun Div.span(block: Span.() -> Unit) {}
// الاستخدام - مع @DslMarker، لا يمكن الوصول العرضي
val page = html {
body {
div {
span {
text("نص داخل span")
}
}
}
}
println(page)
// هذا لن يُترجم (محظور بواسطة @DslMarker):
// html {
// body {
// span { // خطأ! span غير متاح في سياق body مباشرة
// text("...")
**المخرجات:
TEXT
📖 للعرض فقط
<html><body><div><span>نص داخل span</span></div></body></html>
▶ مثال: DSL للاختبار (Should-style)
KOTLIN
// DSL للاختبارات
@DslMarker
annotation class TestDsl
@TestDsl
class TestSuite(val name: String) {
private val tests = mutableListOf<TestCase>()
private var currentTest: TestCase? = null
fun test(description: String, block: TestCase.() -> Unit) {
val testCase = TestCase(description)
currentTest = testCase
testCase.block()
currentTest = null
tests.add(testCase)
}
@TestDsl
fun assert(block: Assertions.() -> Unit) {
Assertions().block()
}
fun run(): TestReport {
val passed = tests.count { it.passed }
val failed = tests.count { !it.passed }
return TestReport(name, tests.size, passed, failed)
}
}
class TestCase(val description: String) {
var passed: Boolean = false
var errorMessage: String? = null
infix fun shouldEqual(expected: Any?) {
// منطق الاختبار
}
}
class Assertions {
infix fun shouldEqual(expected: Any?) {}
infix fun shouldBe(expected: Any?) {}
infix fun shouldNotBe(expected: Any?) {}
fun fail(message: String) {}
}
data class TestReport(val suite: String, val total: Int, val passed: Int, val failed: Int)
// الاستخدام
val suite = TestSuite("Calculator Tests")
suite.test("1 + 1 should equal 2") {
assert { 1 + 1 shouldEqual 2 }
passed = true
}
suite.test("2 * 3 should equal 6") {
assert { 2 * 3 shouldEqual 6 }
passed = true
}
suite.test("this test should fail") {
assert { 1 + 1 shouldEqual 3 } // خاطئ
passed = false
}
println(suite.run())
**المخرجات:
TEXT
📖 للعرض فقط
TestReport(suite=Calculator Tests, total=3, passed=2, failed=1)
▶ مثال: DSL لتكوين الكائنات
KOTLIN
// DSL لبناء Configuration
class DatabaseConfig {
var host: String = "localhost"
var port: Int = 5432
var database: String = "default"
var username: String = "admin"
var password: String = ""
var poolSize: Int = 10
var timeout: Long = 30000
val properties = mutableMapOf<String, String>()
fun connectString(): String = "jdbc:postgresql://$host:$port/$database"
}
fun database(block: DatabaseConfig.() -> Unit): DatabaseConfig {
val config = DatabaseConfig()
config.block()
return config
}
fun DatabaseConfig.property(key: String, value: String) {
properties[key] = value
}
// الاستخدام - DSL واضح وقابل للقراءة
val config = database {
host = "prod-db.example.com"
port = 5433
database = "orders"
username = "app_user"
password = System.getenv("DB_PASSWORD") ?: ""
poolSize = 50
timeout = 60000
property("sslmode", "require")
property("ApplicationName", "OrderProcessor")
}
println("Connect string: ${config.connectString()}")
println("Pool size: ${config.poolSize}")
println("Properties: ${config.properties}")
**المخرجات:
TEXT
📖 للعرض فقط
Connect string: jdbc:postgresql://prod-db.example.com:5433/orders
Pool size: 50
Properties: {sslmode=require, ApplicationName=OrderProcessor}
▶ مثال: type-safe builder pattern
KOTLIN
// DSL لطلبات HTTP آمن من حيث الأنواع
sealed class HttpMethod {
object GET : HttpMethod()
object POST : HttpMethod()
object PUT : HttpMethod()
object DELETE : HttpMethod()
}
class HttpRequest(
val method: HttpMethod,
val url: String,
val headers: Map<String, String> = emptyMap(),
val body: String? = null
)
class HttpRequestBuilder {
var method: HttpMethod = HttpMethod.GET
var url: String = ""
private val headers = mutableMapOf<String, String>()
private var bodyContent: String? = null
fun header(key: String, value: String) {
headers[key] = value
}
fun body(content: String) {
bodyContent = content
}
fun build(): HttpRequest {
require(url.isNotBlank()) { "URL cannot be blank" }
return HttpRequest(method, url, headers.toMap(), bodyContent)
}
}
fun request(block: HttpRequestBuilder.() -> Unit): HttpRequest {
return HttpRequestBuilder().apply(block).build()
}
// الاستخدام
val req = request {
method = HttpMethod.POST
url = "https://api.example.com/orders"
header("Content-Type", "application/json")
header("Authorization", "Bearer xxx")
body("""{"orderId": "ORD-001"}""")
}
println("Request:")
println(" Method: ${req.method::class.simpleName}")
println(" URL: ${req.url}")
println(" Headers: ${req.headers}")
println(" Body: ${req.body}")
**المخرجات:
TEXT
📖 للعرض فقط
Request:
Method: POST
URL: https://api.example.com/orders
Headers: {Content-Type=application/json, Authorization=Bearer xxx}
Body: {"orderId": "ORD-001"}
▶ مثال: infix function للـ DSL
KOTLIN
// استخدام infix functions لبناء DSL أنيق
class OrderBuilder {
val items = mutableListOf<OrderItem>()
var shipping: Shipping = Shipping.STANDARD
var discount: Double = 0.0
}
class OrderItem(val name: String, val price: Double, var quantity: Int = 1)
enum class Shipping { STANDARD, EXPRESS, OVERNIGHT }
// infix functions
infix fun OrderBuilder.item(item: OrderItem) {
items.add(item)
}
infix fun OrderBuilder.with(s: Shipping) {
shipping = s
}
infix fun OrderBuilder.appliedDiscount(percent: Double) {
discount = percent
}
infix fun OrderBuilder.processedWith(action: () -> Unit) {
action()
}
// DSL
val order = OrderBuilder().apply {
item("Laptop" to 1299.99)
item("Mouse" to 29.99)
item("Keyboard" to 79.99)
with Shipping.EXPRESS
appliedDiscount 10.0 // 10% خصم
}
val subtotal = order.items.sumOf { it.price * it.quantity }
val discountAmount = subtotal * order.discount / 100
val total = subtotal - discountAmount
println("Items: ${order.items.size}")
println("Shipping: ${order.shipping}")
println("Subtotal: \$$subtotal")
println("Discount: \$$discountAmount")
println("Total: \$$total")
**المخرجات:
TEXT
📖 للعرض فقط
Items: 3
Shipping: EXPRESS
Subtotal: $1409.97
Discount: $140.997
Total: $1268.973
▶ مثال: Gradle Kotlin DSL style
KOTLIN
// محاكاة DSL لـ Gradle-style
class Dependencies {
private val deps = mutableMapOf<String, String>()
infix fun String.version(v: String) {
deps[this] = v
}
fun implementation(dep: String) {
println("Implementation: $dep")
}
fun testImplementation(dep: String) {
println("Test implementation: $dep")
}
}
class Project {
val dependencies = Dependencies()
var version: String = "1.0.0"
var group: String = "com.example"
}
fun project(block: Project.() -> Unit): Project {
val p = Project()
p.block()
return p
}
// الاستخدام - شبيه بـ build.gradle.kts
val myProject = project {
group = "com.example"
version = "1.0.0"
dependencies {
"kotlinx-coroutines-core" version "1.7.3"
"kotlinx-serialization-json" version "1.6.0"
implementation("org.slf4j:slf4j-api:2.0.9")
testImplementation("org.junit.jupiter:junit-jupiter:5.10.1")
}
}
println("\nProject: ${myProject.group}:${myProject.version}")
**المخرجات:
TEXT
📖 للعرض فقط
Implementation: org.slf4j:slf4j-api:2.0.9
Test implementation: org.junit.jupiter:junit-jupiter:5.10.1
Project: com.example:1.0.0
❓ أسئلة شائعة
س ما الفرق بين DSL وواجهة برمجة تطبيقات عادية؟
ج DSL يهدف إلى "أن يُقرأ كلغة طبيعية أو إعدادات"، محقق عبر مستقبلات لامبدا + دوال امتداد + استدعاءات infix. واجهات البرمجة العادية تعطي الأولوية للوظيفة على المقروئية.
س هل @DslMarker إلزامي؟
ج غير إلزامي، لكن موصى به بشدة. بدونه، يمكن لنطاقات DSL المتداخلة الوصول عرضياً لأعضاء من النطاقات الخارجية، مما يؤدي لأخطاء. @DslMarker يمنع هذا وقت الترجمة.
س ما الفرق بين مستقبلات لامبدا ومعاملات لامبدا؟
ج المستقبلات تُنصل عبر
this (ضمني)؛ المعاملات عبر it (صريح). المستقبلات تناسب DSL (this الضمني يبدو أكثر طبيعية)؛ المعاملات تناسب الاستدعاءات (it الصريح أوضح).س ما أداء DSL؟
ج DSL تنشئ كائنات Builder، التي تتحمل حمل تخصيص طفيف. لكن في سيناريوهات الإعدادات (ليست مسارات ساخنة)، هذا مهمل. تجنب استخدام DSL في حلقات داخلية بملايين الاستدعاءات في الثانية.
س ما الفرق بين DSL كوتلن و DSL Groovy؟
ج DSL كوتلن يمتلك فحص أنواع وقت الترجمة وإكمال IDE التلقائي ودعم إعادة الهيكلة. DSL Groovy مرن ديناميكياً لكنه يفتقد فحوصات وقت الترجمة. Gradle Kotlin DSL يستبدل تدريجياً Groovy DSL.
س كيف أضيف منطقاً شرطياً لـ DSL؟
ج DSL هي نفسها كود كوتلن — يمكنك استخدام
if/when/for وغيرها من تدفق التحكم داخل الكتل. هذه ميزة كبيرة على ملفات الإعدادات — إعدادات + منطق في واحد.📖 ملخص
- مستقبلات لامبدا
Type.() -> Unitهي قلب DSL — استخدمthisللوصول ضمني للمستقبل داخل الكتلة applyيرجع المستقبل (إعداد قابل للتسلسل)؛run/with/letيرجع نتيجة الكتلة- نمط البنّاء + مستقبلات لامبدا = DSL آمن الأنواع
@DslMarkerيمنع تسريب نطاق DSL المتداخل، مفروض وقت الترجمة- DSL = مرونة الإعدادات + أمان الأنواع في الكود
- Gradle Kotlin DSL هو أنجح تطبيق صناعي لـ DSL
📝 تمارين
- مبتدئ (⭐): استخدم
applyلإعداد كائنOrder، تعيين id و total و status. تلميح:order.apply { id = "ORD-001"; total = 299.99 } - متوسط (⭐⭐): ابنِ DSL لـ
EmailBuilder:email { from("a@b.com"); to("c@d.com"); subject("Hi") }. تلميح:fun email(block: EmailBuilder.() -> Unit)+ مستقبل لامبدا - متقدم (⭐⭐⭐): استخدم
@DslMarkerلتنفيذ DSL متداخل:order { customer { name("Alice") }; item { sku("ABC") } }، مع ضمان عدم وصول النطاقات الداخلية لأعضاء خارجية. تلميح:@DslMarker+ بنّاء متعدد الطبقات