Protección del estado
Parte de la sección Programación orientada a objetos del Journey de Ruby de Coddy. Lección 20 de 57.
Un objeto debería rechazar las solicitudes que romperían su estado. raise detiene el método antes de que cambie nada. Tu propia clase de error identifica el problema con precisión; < StandardError hace que la clase sea un tipo de error (herencia, 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.balanceSalida:
InsufficientFunds: only 50 available
50Las clases de error separadas permiten que el llamador reaccione de manera diferente a cada problema, con un rescue por clase. Declara las clases de error antes de las clases que las generan:
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}"
endSalida:
charged 40
fix the input: amount must be positive
try later: limit is 100freeze convierte un objeto en de solo lectura: después de hacerlo, cualquier cambio en sus variables de instancia genera FrozenError, mientras que la lectura sigue funcionando. frozen? indica si un 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.xSalida:
true
FrozenError
1Un lector que devuelve un arreglo interno entrega el arreglo real, por lo que el código externo podría modificarlo. Devuelve una copia congelada con @items.dup.freeze o @items.dup para mantener el control del objeto sobre sus propios datos:
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.songsSalida:
songs are read-only
["Intro"]Desafío
FácilDeclara las clases de error InvalidAmount e InsufficientFunds, ambas subclases de StandardError, y completa Account. deposit(amount) y withdraw(amount) generan InvalidAmount con amount must be positive para cantidades inferiores a 1. withdraw genera InsufficientFunds con balance is only 20 cuando la cantidad es mayor que el saldo. No cambia nada cuando se genera un error.
El código proporcionado lee comandos dep n o wd n e imprime balance 70 después de cada uno, invalid: amount must be positive para InvalidAmount y refused: balance is only 20 para InsufficientFunds.
Tu código va en invalid_amount.rb, insufficient_funds.rb y account.rb. main.rb contiene el código proporcionado de entrada/salida y no se puede editar.
Pruébalo tú mismo
require_relative 'invalid_amount'
require_relative 'insufficient_funds'
require_relative 'account'
# Código de entrada/salida suministrado: déjalo tal 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 lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.
Todas las lecciones de Programación orientada a objetos
1Clases y objetos
Trabajar con archivosDefinir clasesVariables de instanciaMétodos y selfto_s e inspectRepaso: tarjeta de biblioteca4Encapsulación
Métodos privadosMétodos protegidosAsignadores privadosProtección del estadoRepaso: máquina expendedora7Polimorfismo y tipado pato
PolimorfismoTipado patoMétodos abstractoscase y clasesRepaso: métodos de pago2Accesores y self
Lectores y escritoresSetters personalizadosself en los métodosIdentidad e igualdadRepaso: lista de contactos5Herencia
SubclasesSobrescritura de métodosLlamar a superis_a? y ancestrosRepaso - Nómina del personal8Métodos especiales
Igualdad y hashMétodos de operadoresIndexación y adiciónColecciones personalizadasRepaso: fracciones11Proyecto: Gestión de biblioteca
Libros y miembrosPréstamo de librosPractica por tu cuenta: Compilador de Ruby online