Oluşturucu Deseni
Coddy'nin Kotlin Journey'sinin Nesne Yönelimli Programlama bölümünün bir parçası. Ders 48 / 57.
oluşturucu kalıbı bir nesneyi adım adım oluşturur ve en sonunda bütününü denetler. Oluşturucu, parçaları oluşturucunun kendisini döndüren zincirlenebilir metotlarda toplar ve build() tamamlanmış nesneyi oluşturur:
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 içinde:
val mail = EmailBuilder().to("ada@x.io").to("bo@x.io").subject("Lunch").build()
println(mail)Çıktı:
Email(to=[ada@x.io, bo@x.io], subject=Lunch)apply her adımı kısaltır: nesneyi this olarak kullanarak bir blok çalıştırır ve nesneyi döndürür; böylece bir adım tek satır hâline gelir. check(condition) { message }, nesnenin durumu için require karşılığı olan IllegalStateException fırlatır:
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 içinde:
val request = RequestBuilder().apply {
url = "/users"
method = "POST"
header("Accept", "json")
}.build()
println(request)Çıktı:
POST /users [Accept: json]Alıcıya sahip lambda alan, Builder.() -> Unit türündeki bir işlev, oluşturucuyu küçük bir DSL'ye (alana özgü dil) dönüştürür: süslü parantezlerin içinde oluşturucu this'tir; bu nedenle çağıran yalnızca yapılandırmayı yazar:
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 içinde:
val lunch = menu("Lunch") {
item("soup", 450)
item("salad", 600)
}
println(lunch)Çıktı:
== Lunch ==
soup 4.50
salad 6.00Kotlin'in adlandırılmış ve varsayılan bağımsız değişkenleri basit durumları zaten kapsar: Pizza(size = 30, cheese = true) çağrı noktasındaki her seçeneği adlandırır. Bir oluşturucu, nesne birkaç adımda oluşturulduğunda, listeler topladığında veya var olabilmesi için alanlar arasında kontroller yapılması gerektiğinde işe yarar:
data class Pizza(val size: Int = 26, val cheese: Boolean = true, val olives: Boolean = false)
val simple = Pizza(size = 30, olives = true) // builder'a gerek yok
val mail = EmailBuilder() // adım adım birleştirme için bir builder
.to("ada@x.io")
.to("bo@x.io") // bir liste toplar
.subject("Plan")
.build() // sonunda her şeyi kontrol ederGörev
KolaySağlanan Email veri sınıfı için bir builder yazın. EmailBuilder zincirlenebilir adımlara sahiptir: to(address) (birkaç kez çağrılabilir ve alıcıları ekler), subject(text), body(text) ve urgent(). build() bir Email döndürür veya no recipients ya da no subject ile IllegalStateException fırlatır (kontroller bu sırayla yapılır). Acil bir e-postanın konu satırının önüne [URGENT] eklenir.
Sağlanan kod komutları (to ada@x.io, subject Hello, body text, urgent, send) okur, her --- satırından sonra yeni bir builder başlatır ve oluşturulan e-postayı, To: ada@x.io, bo@x.io | [URGENT] Hello | text değerini veya error: no subject yazdırır.
Kodunuz EmailBuilder.kt ve Email.kt dosyalarına gelmelidir. Main.kt sağlanan girdi/çıktı kodunu içerir ve düzenlenemez.
Kendin dene
fun main() {
// Sağlanan girdi/çıktı kodu: olduğu gibi bırakın
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))
}
}
}
Bu ders kısa bir quiz içerir. Soruları yanıtlamak ve ilerlemeni kaydetmek için derse başla.
Nesne Yönelimli Programlama bölümündeki tüm dersler
1Sınıflar ve Nesneler
Dosyalarla ÇalışmaSınıfları TanımlamaBirincil Yapıcılarinit Blokları ve Doğrulamaİkincil YapıcılarTekrar - Kütüphane Kartı4Kalıtım
Açık Sınıflar ve Alt SınıflarÜyeleri Geçersiz Kılmasuper ve YapıcılarSoyut SınıflarTekrar - Personel Dizini2Özellikler ve Kapsülleme
Özel ErişimcilerGörünürlük DeğiştiricileriÖzel Setter'larlazy ve lateinitTekrar - Banka Hesabı5Arayüzler
Arayüz BildirimleriVarsayılan MetotlarBirden Çok ArayüzTür Olarak ArayüzlerÖzet - Ödeme Yöntemleri8Generic'ler
Generic FonksiyonlarGeneric SınıflarTür Kısıtlamalarıin ve out ile VaryansTekrar - Öncelik Kuyruğu11Proje: Kütüphane Yönetimi
Kitaplar ve ÜyelerKitap Ödünç AlmaKendi başına pratik yap: Kotlin playground