Ochrona stanu
Część sekcji Programowanie obiektowe ścieżki Swift w Coddy. Lekcja 19 z 57.
Niezmiennik to reguła, która musi być zawsze spełniona dla danej wartości, na przykład „saldo nigdy nie jest ujemne”. Typ chroni swoje niezmienniki, sprawdzając każdą zmianę przed jej zastosowaniem:
struct BankAccount {
private(set) var balance = 0
mutating func withdraw(_ amount: Int) -> Bool {
guard amount > 0, amount <= balance else { return false }
balance -= amount
return true
}
mutating func deposit(_ amount: Int) {
guard amount > 0 else { return }
balance += amount
}
}
var acc = BankAccount()
acc.deposit(100)
print(acc.withdraw(150))
print(acc.withdraw(40))
print(acc.balance)Wynik:
false
true
60Gdy wywołujący potrzebuje wiedzieć dlaczego zmiana została odrzucona, metoda może zgłosić błąd zamiast zwracać false. Typ błędu jest wyliczeniem zgodnym z Error:
enum TransferError: Error {
case invalidAmount
case insufficientFunds(missing: Int)
}
struct Account {
private(set) var balance: Int
mutating func withdraw(_ amount: Int) throws {
guard amount > 0 else { throw TransferError.invalidAmount }
guard amount <= balance else { throw TransferError.insufficientFunds(missing: amount - balance) }
balance -= amount
}
}
var acc = Account(balance: 30)
do {
try acc.withdraw(50)
} catch TransferError.insufficientFunds(let missing) {
print("missing \(missing)")
} catch {
print("other error")
}Wynik:
missing 20Umieszczaj każdą zmianę chronionych danych wewnątrz metod, które je sprawdzają. Gdyby właściwość można było ustawiać z zewnątrz, dowolna linia kodu mogłaby naruszyć tę zasadę, a typ by tego nie wykrył:
struct Temperature {
private(set) var kelvin: Double // musi pozostać >= 0
mutating func cool(by amount: Double) {
kelvin = max(0, kelvin - amount) // jedyny sposób, by obniżyć tę wartość
}
}Operacja, która zmienia kilka właściwości, musi zachować ich spójność nawet wtedy, gdy zakończy się niepowodzeniem w połowie. Najpierw wszystko sprawdź, a dopiero potem zmień stan:
struct Inventory {
private(set) var stock: [String: Int] = ["tea": 2, "jam": 1]
mutating func sell(_ items: [String]) -> Bool {
var needed: [String: Int] = [:]
for i in items { needed[i, default: 0] += 1 }
for (item, n) in needed where (stock[item] ?? 0) < n { return false }
for (item, n) in needed { stock[item]! -= n }
return true
}
}
var inv = Inventory()
print(inv.sell(["tea", "jam", "jam"]))
print(inv.stock["tea"]!)
print(inv.sell(["tea", "jam"]))
print(inv.stock["tea"]!)Wynik:
false
2
true
1Wyzwanie
ŁatwyUzupełnij strukturę Seats, która sprzedaje bilety na przedstawienie z capacity miejscami. sold ma modyfikator private(set). buy(_:) zgłasza TicketError.invalidCount, gdy liczba jest mniejsza niż 1, TicketError.soldOut, gdy nie zostało już żadne miejsce, oraz TicketError.notEnough(left:), gdy pozostało mniej miejsc, niż zażądano; w przeciwnym razie dodaje bilety do sold.
Dostarczony kod odczytuje pojemność i jedną żądaną liczbę w każdym wierszu. Dla każdej z nich wypisuje sold 3, left 7, invalid count, sold out lub only 2 left.
Twój kod umieść w plikach TicketError.swift i Seats.swift. Plik main.swift zawiera dostarczony kod wejścia/wyjścia i nie można go edytować.
Spróbuj swoich sił
// Dostarczony kod wejścia/wyjścia: pozostaw go bez zmian
var input: [String] = []
while let line = readLine() { input.append(line) }
var seats = Seats(capacity: Int(input[0])!)
for line in input.dropFirst() {
let n = Int(line)!
do {
try seats.buy(n)
print("sold \(n), left \(seats.capacity - seats.sold)")
} catch TicketError.invalidCount {
print("invalid count")
} catch TicketError.soldOut {
print("sold out")
} catch TicketError.notEnough(let left) {
print("only \(left) left")
} catch {
print("unexpected")
}
}
Ta lekcja zawiera krótki quiz. Zacznij lekcję, żeby na niego odpowiedzieć i śledzić swoje postępy.
Wszystkie lekcje w sekcji Programowanie obiektowe
1Klasy i obiekty
Praca z plikamiDefiniowanie klasMetody i selfInicjalizatoryKlasy są referencjamiPowtórzenie — karta biblioteczna4Hermetyzacja
Kontrola dostępuTylko do odczytu z zewnątrzOchrona stanuInicjalizatory, które mogą zawieśćPodsumowanie — konto bankowe7Polimorfizm i typy generyczne
PolimorfizmRzutowanie typówFunkcje generyczneTypy generycznePodsumowanie – kalkulator kształtów2Struktury i typy wartościowe
StrukturySemantyka wartościMetody modyfikująceStruktura czy klasaPodsumowanie – element inwentarza5Dziedziczenie
PodklasyPrzesłanianie metodInicjalizatory i superfinal i hierarchiePodsumowanie — hierarchia pracowników8Standardowe protokoły
CustomStringConvertibleEquatableComparableHashablePodsumowanie — własne sortowanie11Projekt: Zarządzanie biblioteką
Książki i czytelnicyWypożyczanie książekPoćwicz samodzielnie: Edytor online Swift