نمط الباني
جزء من قسم البرمجة كائنية التوجه في رحلة Kotlin على Coddy. الدرس 48 من 57.
ينشئ نمط الباني كائنًا خطوةً بخطوة ويتحقق من الأمر بأكمله في النهاية. يجمع الباني الأجزاء في أساليب قابلة للتسلسل تُرجع الباني نفسه، وينشئ build() الكائن المكتمل:
data class Email(val to: List<String>, val subject: String)
class EmailBuilder {
private val recipients = mutableListOf<String>()
private var subject = ""
fun to(address: String): EmailBuilder {
recipients.add(address)
return this
}
fun subject(text: String): EmailBuilder {
subject = text
return this
}
fun build(): Email {
check(recipients.isNotEmpty()) { "no recipients" }
return Email(recipients.toList(), subject)
}
}داخل main:
val mail = EmailBuilder().to("ada@x.io").to("bo@x.io").subject("Lunch").build()
println(mail)الناتج:
Email(to=[ada@x.io, bo@x.io], subject=Lunch)تختصر apply كل خطوة: فهي تشغّل كتلة مع الكائن بوصفه this وتُعيد الكائن، لذا تصبح الخطوة سطرًا واحدًا. ترمي check(condition) { message } استثناءً من نوع IllegalStateException، وهو النظير لـ require لحالة الكائن:
class RequestBuilder {
var url = ""
var method = "GET"
private val headers = mutableListOf<String>()
fun header(name: String, value: String) = apply { headers.add("$name: $value") }
fun build(): String {
check(url.isNotEmpty()) { "no url" }
return "$method $url " + headers
}
}داخل main:
val request = RequestBuilder().apply {
url = "/users"
method = "POST"
header("Accept", "json")
}.build()
println(request)الناتج:
POST /users [Accept: json]إن الدالة التي تأخذ لامبدا مع مُستقبِل، من النوع Builder.() -> Unit، تحوّل المُنشئ إلى DSL صغيرة (لغة خاصة بالمجال): داخل الأقواس المعقوفة، يكون المُنشئ هو this، لذا لا يكتب المستدعي سوى الإعدادات:
class MenuBuilder(private val title: String) {
private val items = mutableListOf<String>()
fun item(name: String, cents: Int) {
items.add("$name %d.%02d".format(cents / 100, cents % 100))
}
fun build() = "== $title ==\n" + items.joinToString("\n")
}
fun menu(title: String, block: MenuBuilder.() -> Unit): String = MenuBuilder(title).apply(block).build()داخل main:
val lunch = menu("Lunch") {
item("soup", 450)
item("salad", 600)
}
println(lunch)الناتج:
== Lunch ==
soup 4.50
salad 6.00تغطي الوسائط المُسمّاة والافتراضية في Kotlin الحالات البسيطة بالفعل: يحدّد Pizza(size = 30, cheese = true) كل اختيار في موضع الاستدعاء. يكون المُنشئ مفيدًا عندما يُجمَّع الكائن عبر عدة خطوات، أو يجمع قوائم، أو يحتاج إلى إجراء عمليات تحقق عبر الحقول قبل أن يصبح موجودًا:
data class Pizza(val size: Int = 26, val cheese: Boolean = true, val olives: Boolean = false)
val simple = Pizza(size = 30, olives = true) // لا حاجة إلى builder
val mail = EmailBuilder() // builder للتجميع خطوة بخطوة
.to("ada@x.io")
.to("bo@x.io") // يجمع قائمة
.subject("Plan")
.build() // يتحقق من كل شيء في النهايةالتحدي
سهلاكتب بنّاءً لفئة البيانات المعطاة Email. يحتوي EmailBuilder على خطوات قابلة للتسلسل: to(address) (يمكن استدعاؤها عدة مرات وتضيف مستلمين)، وsubject(text)، وbody(text)، وurgent(). تُرجع build() كائنًا من نوع Email أو ترمي IllegalStateException مع no recipients أو no subject (بهذا الترتيب من عمليات التحقق). يُضاف إلى مقدمة موضوع البريد الإلكتروني العاجل [URGENT] .
يقرأ الكود المعطى أوامر (to ada@x.io، وsubject Hello، وbody text، وurgent، وsend)، ويبدأ بنّاءً جديدًا بعد كل سطر ---، ويطبع البريد الإلكتروني المُنشأ، To: ada@x.io, bo@x.io | [URGENT] Hello | text، أو error: no subject.
ضع الكود الخاص بك في EmailBuilder.kt وEmail.kt. يحتوي Main.kt على كود الإدخال/الإخراج المعطى ولا يمكن تعديله.
جرّب بنفسك
fun main() {
// كود الإدخال/الإخراج المُزوَّد: أبقِه كما هو
val input = generateSequence(::readLine).toList()
var builder = EmailBuilder()
for (cmd in input) {
when {
cmd == "send" -> {
try {
println(builder.build())
} catch (e: IllegalStateException) {
println("error: ${e.message}")
}
}
cmd == "---" -> builder = EmailBuilder()
cmd == "urgent" -> builder.urgent()
cmd.startsWith("to ") -> builder.to(cmd.drop(3))
cmd.startsWith("subject ") -> builder.subject(cmd.drop(8))
else -> builder.body(cmd.drop(5))
}
}
}
يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.
جميع دروس البرمجة كائنية التوجه
1الفئات والكائنات
العمل مع الملفاتتعريف الفئاتالمنشئات الأساسيةكتل init والتحقق من الصحةالمنشئات الثانويةمراجعة - بطاقة المكتبة4الوراثة
الأصناف المفتوحة والأصناف الفرعيةتجاوز الأعضاءsuper والمنشئاتالأصناف المجرّدةمراجعة - دليل الموظفينتدرّب بنفسك: Playground لـ Kotlin