Menu
CoddyTech

Pola Status

Termasuk bagian Pemrograman Berorientasi Objek dalam perjalanan Swift di Coddy. Pelajaran 49 dari 57.

Pola state memungkinkan object mengubah perilakunya saat state internalnya berubah. Setiap state memiliki tipenya sendiri yang menentukan apa yang terjadi dan state mana yang muncul berikutnya:

protocol LightState {
    var name: String { get }
    func next() -> any LightState
}
struct Red: LightState { var name: String { "red" }; func next() -> any LightState { Green() } }
struct Green: LightState { var name: String { "green" }; func next() -> any LightState { Yellow() } }
struct Yellow: LightState { var name: String { "yellow" }; func next() -> any LightState { Red() } }

var light: any LightState = Red()
for _ in 1...4 {
    print(light.name)
    light = light.next()
}

Output:

red
green
yellow
red

object yang memiliki state meneruskan permintaan ke state saat ini. Setiap state menangani sebuah peristiwa dengan caranya sendiri, dan tindakan ilegal ditolak oleh state yang tidak mengizinkannya:

protocol OrderState {
    var name: String { get }
    func pay() -> (any OrderState)?
    func ship() -> (any OrderState)?
}
struct New: OrderState { var name: String { "new" }; func pay() -> (any OrderState)? { Paid() }; func ship() -> (any OrderState)? { nil } }
struct Paid: OrderState { var name: String { "paid" }; func pay() -> (any OrderState)? { nil }; func ship() -> (any OrderState)? { Shipped() } }
struct Shipped: OrderState { var name: String { "shipped" }; func pay() -> (any OrderState)? { nil }; func ship() -> (any OrderState)? { nil } }

final class Order {
    private(set) var state: any OrderState = New()
    func handle(_ event: String) -> String {
        guard let next = event == "pay" ? state.pay() : state.ship() else { return "cannot \(event) when \(state.name)" }
        state = next
        return "now \(state.name)"
    }
}

let order = Order()
for e in ["ship", "pay", "pay", "ship"] { print(order.handle(e)) }

Output:

cannot ship when new
now paid
cannot pay when paid
now shipped

Untuk sejumlah kecil state tanpa datanya sendiri, enum dengan switch sering kali menyampaikan gagasan yang sama dengan lebih ringkas, sementara compiler memeriksa bahwa setiap state ditangani:

enum Door {
    case open, closed, locked

    func handle(_ action: String) -> Door? {
        switch (self, action) {
        case (.closed, "open"): return .open
        case (.open, "close"): return .closed
        case (.closed, "lock"): return .locked
        case (.locked, "unlock"): return .closed
        default: return nil
        }
    }
}

var door = Door.closed
for a in ["lock", "open", "unlock", "open"] {
    if let next = door.handle(a) { door = next; print("\(a) -> \(door)") } else { print("cannot \(a)") }
}

Output:

lock -> locked
cannot open
unlock -> closed
open -> open

Tipe state bermanfaat saat setiap state memiliki beberapa perilaku atau datanya sendiri. Dalam kedua kasus, aturan untuk setiap state berada di satu tempat, bukan tersebar dalam pemeriksaan if di seluruh object.

// tersebar: setiap method memeriksa state
func ship() { if status == "paid" { ... } else if status == "new" { ... } }
// pola state: setiap state merespons sendiri
state = state.ship() ?? state
challenge icon

Tantangan

Mudah

Modelkan mesin penjual otomatis dengan state enum Vending yang memiliki case idle, hasCoin(Int) dan soldOut. handle(_ event:) mengembalikan state berikutnya atau nil untuk event yang tidak diizinkan: coin <n> dalam idle beralih ke hasCoin(n), dan dalam hasCoin menambahkan jumlahnya; buy dalam hasCoin dengan nilai setidaknya 150 beralih ke idle (penjualan berhasil); refund dalam hasCoin beralih ke idle; empty dalam state apa pun beralih ke soldOut; refill dalam soldOut beralih ke idle.

Kode yang disediakan menerapkan event secara berurutan dan mencetak state setelah setiap event (idle, coin 100, sold out) atau not allowed: <event>.

Letakkan kodemu di Vending.swift. main.swift berisi kode input/output yang disediakan dan tidak boleh diedit.

Coba sendiri

// Kode input/output yang disediakan: biarkan apa adanya
var input: [String] = []
while let line = readLine() { input.append(line) }
var machine = Vending.idle
for event in input {
    if let next = machine.handle(event) {
        machine = next
        print(machine.label)
    } else {
        print("not allowed: \(event)")
    }
}
quiz iconUji dirimu

Pelajaran ini berisi kuis singkat. Mulai pelajarannya untuk menjawab kuis dan melacak progresmu.

Semua pelajaran di Pemrograman Berorientasi Objek

Latihan sendiri: Playground Swift