Protegendo o Estado
Parte da seção Programação Orientada a Objetos do Journey de Swift da Coddy. Lição 19 de 57.
Um invariante é uma regra que deve sempre ser válida para um valor, como “um saldo nunca é negativo”. Um tipo protege seus invariantes verificando cada alteração antes de aplicá-la:
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)Saída:
false
true
60Quando quem chama precisa saber por que uma alteração foi recusada, o método pode lançar um erro em vez de retornar false. O tipo do erro é um enum que está em conformidade com 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")
}Saída:
missing 20Mantenha toda alteração nos dados protegidos dentro de métodos que os verifiquem. Se a propriedade pudesse ser definida externamente, qualquer linha de código poderia violar a regra sem que o tipo percebesse:
struct Temperature {
private(set) var kelvin: Double // deve permanecer >= 0
mutating func cool(by amount: Double) {
kelvin = max(0, kelvin - amount) // a única forma de baixá-lo
}
}Uma operação que altera várias propriedades deve mantê-las consistentes mesmo quando falha no meio. Verifique tudo primeiro e, depois, altere o estado:
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"]!)Saída:
false
2
true
1Desafio
FácilComplete a struct Seats, que vende ingressos para um espetáculo com capacity assentos. sold é private(set). buy(_:) lança TicketError.invalidCount para uma quantidade menor que 1, TicketError.soldOut quando não resta nenhum assento e TicketError.notEnough(left:) quando restam menos assentos do que o solicitado; caso contrário, adiciona os ingressos a sold.
O código fornecido lê a capacidade e uma quantidade solicitada por linha. Para cada uma, ele imprime sold 3, left 7, invalid count, sold out ou only 2 left.
Seu código deve ficar em TicketError.swift e Seats.swift. main.swift contém o código fornecido de entrada/saída e não pode ser editado.
Experimente você mesmo
// Código de entrada/saída fornecido: mantenha-o como está
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")
}
}
Esta lição inclui um quiz rápido. Comece a lição para respondê-lo e acompanhar seu progresso.
Todas as lições de Programação Orientada a Objetos
1Classes e Objetos
Trabalhando com ArquivosDefinindo ClassesMétodos e selfInicializadoresClasses são ReferênciasRevisão - Cartão da Biblioteca4Encapsulamento
Controle de AcessoSomente Leitura ExternaProtegendo o EstadoInicializadores que Podem FalharRevisão - Conta Bancária7Polimorfismo e Genéricos
PolimorfismoConversão de TiposFunções GenéricasTipos GenéricosRevisão - Calculadora de Formas2Structs e Tipos de Valor
StructsSemântica de ValorMétodos MutáveisStruct ou ClasseRecapitulação - Item de Inventário5Herança
SubclassesSobrescrita de métodosInicializadores e superfinal e hierarquiasRecapitulação - Hierarquia de funcionários8Protocolos Padrão
CustomStringConvertibleEquatableComparableHashableRecapitulação - Ordenação Personalizada11Projeto: Gerenciamento de Biblioteca
Livros e MembrosEmpréstimo de LivrosPratique por conta própria: Playground de Swift