Durum Deseni
Coddy'nin Kotlin Journey'sinin Nesne Yönelimli Programlama bölümünün bir parçası. Ders 49 / 57.
state deseni, bir nesneye içinde bulunduğu her durum için farklı bir davranış kazandırır. Her durum kendi türüdür ve nesne her olayı mevcut durumuna iletir; mevcut durum da bir sonraki durumu döndürür:
interface LampState {
val name: String
fun press(): LampState
}
object Off : LampState {
override val name = "off"
override fun press() = Dim
}
object Dim : LampState {
override val name = "dim"
override fun press() = Bright
}
object Bright : LampState {
override val name = "bright"
override fun press() = Off
}
class Lamp {
var state: LampState = Off
private set
fun press() { state = state.press() }
}main içinde:
val lamp = Lamp()
repeat(4) {
lamp.press()
println(lamp.state.name)
}Çıktı:
dim
bright
off
dimBir durum veri taşıyabilir. Mühürlü bir arayüz ile data object verisiz bir durumu, bir veri sınıfı verili bir durumu temsil eder ve bir işlev her olay için bir sonraki durumu döndürür:
sealed interface Parking
data object Free : Parking
data class Taken(val plate: String, val since: Int) : Parking
fun Parking.arrive(plate: String, time: Int): Parking = when (this) {
Free -> Taken(plate, time)
is Taken -> this
}
fun Parking.leave(time: Int): Pair<Parking, Int> = when (this) {
Free -> this to 0
is Taken -> Free to (time - since) * 2
}main içinde:
var spot: Parking = Free
spot = spot.arrive("AB12", 9)
println(spot)
val (next, fee) = spot.leave(12)
println("$next, fee $fee")Çıktı:
Taken(plate=AB12, since=9)
Free, fee 6Bazı olaylara bazı durumlarda izin verilmez. Geçiş, bunlar için null döndürebilir; nesne bulunduğu yerde kalır ve reddedildiğini bildirir:
sealed interface Door
data object Open : Door
data object Closed : Door
data object Locked : Door
fun next(door: Door, event: String): Door? = when (door) {
Open -> if (event == "close") Closed else null
Closed -> when (event) { "open" -> Open; "lock" -> Locked; else -> null }
Locked -> if (event == "unlock") Closed else null
}main içinde:
var door: Door = Open
for (e in listOf("lock", "close", "lock", "open", "unlock")) {
val n = next(door, e)
if (n == null) println("$e refused while $door") else door = n
}
println("final: $door")Çıktı:
lock refused while Open
open refused while Locked
final: ClosedNeden birkaç Boolean bayrağı kullanmayalım? isOpen ve isLocked gibi bayraklar, aynı anda açık ve kilitli olmak gibi imkânsız birleşimlere izin verir ve her yöntemin bunları kontrol etmesi gerekir. Tek bir durum özelliği tam olarak bir geçerli durum tutar ve mühürlü bir tür üzerinde kullanılan when, derleyicinin her durumun ele alındığını kontrol etmesini sağlar:
// flags: 4 kombinasyon, bunlardan biri imkânsız
class DoorWithFlags {
var isOpen = false
var isLocked = false // isOpen && isLocked asla gerçekleşmemeli
}
// state: her zaman tam olarak Open, Closed, Locked'dan biri
class DoorWithState {
var state: Door = Closed
private set
}Görev
KolayBir satış makinesi, sağlanan Idle, HasCoins(cents) veya SoldOut durumlarından birindedir. idle, coins 100 veya sold out döndüren Vending.label() işlevini ve state değeri Idle olarak başlayan ve yalnızca sınıfın içinde değiştirilebilen VendingMachine sınıfını yazın. handle(event) bir olayı uygular ve true döndürür; olaya izin verilmiyorsa hiçbir şeyi değiştirmeden false döndürür: coin <n>, Idle durumundan HasCoins(n) durumuna geçer ve HasCoins içindeki miktara ekleme yapar; HasCoins durumunda en az 150 sent ile buy bir satıştır (sales içinde sayılır) ve yeniden Idle durumuna geçer; HasCoins durumunda refund, Idle durumuna geçer; empty, herhangi bir durumdan SoldOut durumuna geçer; SoldOut durumunda refill, Idle durumuna geçer.
Sağlanan kod, olayları sırayla uygular ve her olaydan sonra durumun etiketini ya da not allowed: <event> yazdırır; son olarak sales: 1 yazdırır.
Kodunuz VendingMachine.kt, Label.kt, Vending.kt, Idle.kt, HasCoins.kt ve SoldOut.kt dosyalarına yazılmalıdır. 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()
val machine = VendingMachine()
for (event in input) {
if (machine.handle(event)) println(machine.state.label()) else println("not allowed: $event")
}
println("sales: ${machine.sales}")
}
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