Menu
Coddy logo textTech

Durum Kalıbı

Coddy'nin Swift Journey'sinin Nesne Yönelimli Programlama bölümünün bir parçası. Ders 49 / 57.

Durum deseni, bir nesnenin iç durumu değiştiğinde davranışını değiştirmesini sağlar. Her durum, ne olacağına ve bundan sonra hangi durumun geleceğine karar veren kendi türüdür:

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()
}

Çıktı:

red
green
yellow
red

Duruma sahip olan nesne, istekleri mevcut durumuna iletir. Her durum bir olayı kendi yöntemiyle ele alır ve izin verilmeyen eylemlere, bunlara izin vermeyen durumlar tarafından karşı çıkılır:

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)) }

Çıktı:

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

Kendi verilerine sahip olmayan az sayıda durum için, switch içeren bir enum genellikle aynı fikri daha özlü bir şekilde ifade eder; derleyici de her durumun ele alındığını kontrol eder:

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)") }
}

Çıktı:

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

Durum türleri, her durumun birden fazla davranışı veya kendine ait verileri olduğunda işe yarar. Her iki durumda da her durumun kuralları, nesnenin geneline yayılmış if kontrolleri yerine tek bir yerde bulunur.

// dağınık: her metot durumu kontrol eder
func ship() { if status == "paid" { ... } else if status == "new" { ... } }
// durum deseni: her durum kendi adına cevap verir
state = state.ship() ?? state
challenge icon

Görev

Kolay

idle, hasCoin(Int) ve soldOut durumlarına sahip bir enum durumu Vending ile bir otomat modelleyin. handle(_ event:), bir sonraki durumu veya izin verilmeyen bir etkinlik için nil döndürür: idle durumundaki coin <n>, hasCoin(n) durumuna geçer ve hasCoin durumunda miktara ekleme yapar; hasCoin durumunda en az 150 ile buy, idle durumuna geçer (başarılı bir satış); hasCoin durumunda refund, idle durumuna geçer; herhangi bir durumdaki empty, soldOut durumuna geçer; soldOut durumundaki refill, idle durumuna geçer.

Sağlanan kod, etkinlikleri sırayla uygular ve her birinden sonra durumu (idle, coin 100, sold out) veya not allowed: <event> yazdırır.

Kodunuz Vending.swift dosyasına gelir. main.swift, sağlanan girdi/çıktı kodunu içerir ve düzenlenemez.

Kendin dene

// Sağlanan giriş/çıkış kodu: olduğu gibi bırakın
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 iconKendini test et

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

Kendi başına pratik yap: Swift playground