Zustand schützen
Teil des Abschnitts Objektorientierte Programmierung der Swift-Journey von Coddy. Lektion 19 von 57.
Eine Invariante ist eine Regel, die für einen Wert immer gelten muss, wie etwa „ein Kontostand ist nie negativ“. Ein Typ schützt seine Invarianten, indem er jede Änderung überprüft, bevor er sie anwendet:
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)Ausgabe:
false
true
60Wenn ein Aufrufer wissen muss, warum eine Änderung abgelehnt wurde, kann die Methode einen Fehler auslösen, anstatt false zurückzugeben. Der Fehlertyp ist ein Enum, das Error entspricht:
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")
}Ausgabe:
missing 20Halte jede Änderung der geschützten Daten innerhalb von Methoden, die sie überprüfen. Wenn die Eigenschaft von außen setzbar wäre, könnte jede Codezeile die Regel verletzen, ohne dass der Typ es bemerkt:
struct Temperature {
private(set) var kelvin: Double // muss >= 0 bleiben
mutating func cool(by amount: Double) {
kelvin = max(0, kelvin - amount) // der einzige Weg, es zu senken
}
}Eine Operation, die mehrere Eigenschaften ändert, muss sie konsistent hinterlassen, selbst wenn sie auf halbem Weg fehlschlägt. Überprüfe zuerst alles und ändere dann den Zustand:
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"]!)Ausgabe:
false
2
true
1Aufgabe
EinfachVervollständige das struct Seats, das Tickets für eine Veranstaltung mit capacity Sitzplätzen verkauft. sold ist private(set). buy(_:) wirft TicketError.invalidCount bei einer Anzahl unter 1, TicketError.soldOut, wenn kein Sitzplatz mehr frei ist, und TicketError.notEnough(left:), wenn weniger Sitzplätze frei sind als angefordert; andernfalls fügt es die Tickets zu sold hinzu.
Der bereitgestellte Code liest die Kapazität und eine angeforderte Anzahl pro Zeile ein. Für jede davon gibt er sold 3, left 7, invalid count, sold out oder only 2 left aus.
Dein Code gehört in TicketError.swift und Seats.swift. main.swift enthält den bereitgestellten Eingabe-/Ausgabecode und kann nicht bearbeitet werden.
Probier es selbst
// Gelieferter Ein-/Ausgabecode: belasse ihn wie er ist
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")
}
}
Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.
Alle Lektionen in Objektorientierte Programmierung
1Klassen und Objekte
Arbeiten mit DateienKlassen definierenMethoden und selfInitialisiererKlassen sind ReferenzenRückblick – Bibliotheksausweis4Kapselung
ZugriffskontrolleVon außen schreibgeschütztZustand schützenFehlschlagende InitialisiererRückblick – Bankkonto7Polymorphismus und Generika
PolymorphismusTypumwandlungGenerische FunktionenGenerische TypenZusammenfassung – Shape-Rechner2Strukturen und Werttypen
StrukturenWertsemantikVerändernde MethodenStruktur oder KlasseZusammenfassung – Inventarelement5Vererbung
UnterklassenMethoden überschreibenInitialisierer und superfinal und HierarchienRückblick – Mitarbeiterhierarchie8Standardprotokolle
CustomStringConvertibleEquatableComparableHashableRückblick – Benutzerdefiniertes Sortieren11Projekt: Bibliotheksverwaltung
Bücher und MitgliederBücher ausleihenÜbe selbstständig: Swift-Playground