Pola State
Termasuk bagian Pemrograman Berorientasi Objek dalam perjalanan Kotlin di Coddy. Pelajaran 49 dari 57.
Pola state memberikan perilaku berbeda kepada sebuah object untuk setiap state yang dimilikinya. Setiap state memiliki tipenya sendiri, dan object meneruskan setiap peristiwa ke state saat ini, yang mengembalikan state berikutnya:
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() }
}Di dalam main:
val lamp = Lamp()
repeat(4) {
lamp.press()
println(lamp.state.name)
}Output:
dim
bright
off
dimSebuah state dapat menyimpan data. Dengan sealed interface, data object mewakili state tanpa data, data class mewakili state dengan data, dan satu fungsi mengembalikan state berikutnya untuk setiap peristiwa:
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
}Di dalam main:
var spot: Parking = Free
spot = spot.arrive("AB12", 9)
println(spot)
val (next, fee) = spot.leave(12)
println("$next, fee $fee")Output:
Taken(plate=AB12, since=9)
Free, fee 6Beberapa peristiwa tidak diizinkan pada status tertentu. Transisi dapat mengembalikan null untuk peristiwa tersebut, lalu object tetap berada di tempatnya dan melaporkan penolakan:
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
}Di dalam main:
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")Output:
lock refused while Open
open refused while Locked
final: ClosedMengapa tidak menggunakan beberapa flag Boolean? Flag seperti isOpen dan isLocked memungkinkan kombinasi yang mustahil, seperti terbuka dan terkunci sekaligus, dan setiap method harus memeriksanya. Satu property status hanya menyimpan satu status yang valid, dan penggunaan when pada tipe tersegel membuat compiler memeriksa bahwa setiap status ditangani:
// flag: 4 kombinasi, salah satunya tidak mungkin terjadi
class DoorWithFlags {
var isOpen = false
var isLocked = false // isOpen && isLocked tidak boleh terjadi
}
// state: selalu tepat satu dari Open, Closed, Locked
class DoorWithState {
var state: Door = Closed
private set
}Tantangan
MudahMesin penjual otomatis berada di salah satu state yang disediakan, yaitu Idle, HasCoins(cents) atau SoldOut. Tulis Vending.label(), yang mengembalikan idle, coins 100 atau sold out, serta class VendingMachine, dengan state yang dimulai sebagai Idle dan hanya dapat diubah di dalam class. handle(event) menerapkan suatu event dan mengembalikan true, atau mengembalikan false dan tidak mengubah apa pun jika event tersebut tidak diizinkan: coin <n> mengubah state dari Idle menjadi HasCoins(n) dan menambahkan jumlahnya ke nilai dalam HasCoins; buy dalam HasCoins dengan setidaknya 150 sen merupakan penjualan, yang dihitung dalam sales, dan mengubah state kembali menjadi Idle; refund dalam HasCoins mengubah state menjadi Idle; empty mengubah state menjadi SoldOut dari state apa pun; refill dalam SoldOut mengubah state menjadi Idle.
Kode yang disediakan menerapkan event secara berurutan dan mencetak label state setelah setiap event, atau not allowed: <event>, lalu mencetak sales: 1 di akhir.
Tulis kodemu di VendingMachine.kt, Label.kt, Vending.kt, Idle.kt, HasCoins.kt dan SoldOut.kt. Main.kt berisi kode input/output yang disediakan dan tidak dapat diedit.
Coba sendiri
fun main() {
// Kode input/output yang disediakan: biarkan apa adanya
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}")
}
Pelajaran ini berisi kuis singkat. Mulai pelajarannya untuk menjawab kuis dan melacak progresmu.
Semua pelajaran di Pemrograman Berorientasi Objek
1class dan object
Bekerja dengan fileMendefinisikan classKonstruktor utamaBlok init dan validasiKonstruktor sekunderRingkasan: Kartu Perpustakaan2property dan Enkapsulasi
Accessor KustomModifier VisibilitasSetter Privatlazy dan lateinitRingkasan: Rekening Bank5Interface
Mendeklarasikan Interfacemethod DefaultBeberapa InterfaceInterface sebagai TipeUlasan: Metode Pembayaran11Proyek: Pengelolaan Perpustakaan
Buku dan AnggotaPeminjaman BukuLatihan sendiri: Playground Kotlin