Menu
Coddy logo textTech

Proteggere lo stato

Fa parte della sezione Programmazione orientata agli oggetti del percorso Ruby di Coddy. Lezione 20 di 57.

Un oggetto dovrebbe rifiutare le richieste che ne comprometterebbero lo stato. raise interrompe il metodo prima che qualcosa cambi. Il nome della tua classe di errore descrive il problema con precisione; < StandardError rende la classe un tipo di errore (ereditarietà, capitolo 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.balance

Output:

InsufficientFunds: only 50 available
50

Classi di errore separate permettono a chi chiama di reagire a ogni problema in modo diverso, con un rescue per classe. Dichiara le classi di errore prima delle classi che le sollevano:

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}"
end

Output:

charged 40
fix the input: amount must be positive
try later: limit is 100

freeze rende un oggetto di sola lettura: dopo averlo applicato, qualsiasi modifica alle sue variabili di istanza genera FrozenError, mentre la lettura continua a funzionare. frozen? indica se un oggetto è congelato:

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.x

Output:

true
FrozenError
1

Un lettore che restituisce un array interno espone l'array reale, quindi il codice esterno potrebbe modificarlo. Restituisci una copia congelata con @items.dup.freeze, oppure @items.dup, per mantenere l'oggetto al controllo dei propri dati:

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.songs

Output:

songs are read-only
["Intro"]
challenge icon

Sfida

Facile

Dichiara le classi di errore InvalidAmount e InsufficientFunds, entrambe sottoclassi di StandardError, e completa Account. deposit(amount) e withdraw(amount) sollevano InvalidAmount con amount must be positive per importi inferiori a 1. withdraw solleva InsufficientFunds con balance is only 20 quando l’importo è maggiore del saldo. Quando viene sollevato un errore, non cambia nulla.

Il codice fornito legge i comandi dep n o wd n e stampa balance 70 dopo ciascuno, invalid: amount must be positive per InvalidAmount e refused: balance is only 20 per InsufficientFunds.

Il tuo codice va inserito in invalid_amount.rb, insufficient_funds.rb e account.rb. main.rb contiene il codice fornito per l’input/output e non può essere modificato.

Provalo tu

require_relative 'invalid_amount'
require_relative 'insufficient_funds'
require_relative 'account'

# Código de entrada/salida proporcionado: mantenlo 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
quiz iconMettiti alla prova

Questa lezione include un breve quiz. Inizia la lezione per rispondere e tenere traccia dei tuoi progressi.

Tutte le lezioni di Programmazione orientata agli oggetti

Esercitati da solo: Compilatore Ruby online