Protegendo o estado
Parte da seção Programação orientada a objetos do Journey de Ruby da Coddy. Lição 20 de 57.
Um objeto deve recusar solicitações que quebrariam seu estado. raise interrompe o método antes que qualquer coisa seja alterada. Sua própria classe de erro nomeia o problema com precisão; < StandardError faz com que a classe seja um tipo de erro (herança, capítulo 5):
class InsufficientFunds < StandardError
end
class Account
attr_reader :balance
def initialize(balance)
@balance = balance
end
def withdraw(amount)
raise InsufficientFunds, "only #{balance} available" if amount > balance
@balance -= amount
end
end
account = Account.new(50)
begin
account.withdraw(80)
rescue InsufficientFunds => e
puts "#{e.class}: #{e.message}"
end
puts account.balanceSaída:
InsufficientFunds: only 50 available
50Classes de erro separadas permitem que o chamador reaja a cada problema de maneira diferente, com um rescue por classe. Declare as classes de erro antes das classes que as geram:
class InvalidAmount < StandardError
end
class LimitReached < StandardError
end
class Card
def charge(amount)
raise InvalidAmount, "amount must be positive" unless amount.positive?
raise LimitReached, "limit is 100" if amount > 100
"charged #{amount}"
end
end
[40, -5, 500].each do |amount|
puts Card.new.charge(amount)
rescue InvalidAmount => e
puts "fix the input: #{e.message}"
rescue LimitReached => e
puts "try later: #{e.message}"
endSaída:
charged 40
fix the input: amount must be positive
try later: limit is 100freeze torna um objeto somente para leitura: depois disso, qualquer alteração em suas variáveis de instância gera FrozenError, enquanto a leitura continua funcionando. frozen? informa se um objeto está congelado:
class Point
attr_accessor :x
def initialize(x)
@x = x
end
end
pt = Point.new(1).freeze
puts pt.frozen?
begin
pt.x = 2
rescue FrozenError => e
puts e.class
end
puts pt.xSaída:
true
FrozenError
1Um método de leitura que retorna um array interno entrega o array real, então o código externo poderia alterá-lo. Retorne uma cópia congelada com @items.dup.freeze, ou @items.dup, para manter o objeto no controle dos próprios dados:
class Playlist
def initialize
@songs = []
end
def add(song)
@songs << song
end
def songs
@songs.dup.freeze
end
end
list = Playlist.new
list.add("Intro")
begin
list.songs << "Hack"
rescue FrozenError
puts "songs are read-only"
end
p list.songsSaída:
songs are read-only
["Intro"]Desafio
FácilDeclare as classes de erro InvalidAmount e InsufficientFunds, ambas subclasses de StandardError, e complete Account. deposit(amount) e withdraw(amount) geram InvalidAmount com amount must be positive para valores menores que 1. withdraw gera InsufficientFunds com balance is only 20 quando o valor é maior que o saldo. Nada muda quando um erro é gerado.
O código fornecido lê os comandos dep n ou wd n e exibe balance 70 após cada um, invalid: amount must be positive para InvalidAmount e refused: balance is only 20 para InsufficientFunds.
Seu código deve estar em invalid_amount.rb, insufficient_funds.rb e account.rb. main.rb contém o código fornecido de entrada/saída e não pode ser editado.
Experimente você mesmo
require_relative 'invalid_amount'
require_relative 'insufficient_funds'
require_relative 'account'
# Código de entrada/saída fornecido: mantenha-o como está
input = []
while (line = gets)
input << line.chomp
end
account = Account.new
input.each do |line|
action, amount = line.split
begin
action == "dep" ? account.deposit(amount.to_i) : account.withdraw(amount.to_i)
puts "balance #{account.balance}"
rescue InvalidAmount => e
puts "invalid: #{e.message}"
rescue InsufficientFunds => e
puts "refused: #{e.message}"
end
end
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 ClassesVariáveis de InstânciaMétodos e selfto_s e inspectRevisão - Cartão da Biblioteca4Encapsulamento
Métodos privadosMétodos protegidosSetters privadosProtegendo o estadoRevisão - Máquina de venda automática7Polimorfismo e Duck Typing
PolimorfismoDuck TypingMétodos Abstratoscase e ClassesRevisão - Métodos de Pagamento2Acessores e self
Leitores e GravadoresSetters Personalizadosself nos MétodosIdentidade e IgualdadeRecapitulação - Lista de Contatos5Herança
SubclassesSobrescrita de métodosChamando superis_a? e ancestraisRevisão - Folha de pagamento da equipe8Métodos Especiais
Igualdade e hashMétodos de OperadoresIndexação e AdiçãoColeções PersonalizadasRevisão - Frações11Projeto: Gerenciamento de Biblioteca
Livros e MembrosEmpréstimo de LivrosPratique por conta própria: Compilador de Ruby online