Menu
Coddy logo textTech

Protéger l’état

Fait partie de la section Programmation orientée objet du Journey Ruby de Coddy. Leçon 20 sur 57.

Un objet doit refuser les requêtes qui compromettraient son état. raise arrête la méthode avant que quoi que ce soit ne change. Le nom de votre propre classe d’erreur décrit précisément le problème ; < StandardError fait de la classe un type d’erreur (héritage, chapitre 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

Résultat :

InsufficientFunds: only 50 available
50

Des classes d’erreur distinctes permettent à l’appelant de réagir différemment à chaque problème, avec un rescue par classe. Déclarez les classes d’erreur avant les classes qui les lèvent :

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

Sortie :

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

freeze rend un objet accessible en lecture seule : après cela, toute modification de ses variables d’instance déclenche FrozenError, tandis que la lecture continue de fonctionner. frozen? indique si un objet est gelé :

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

Sortie :

true
FrozenError
1

Un accesseur qui renvoie un tableau interne expose le véritable tableau, de sorte que du code externe pourrait le modifier. Renvoyez une copie gelée avec @items.dup.freeze, ou @items.dup, afin que l’objet garde le contrôle de ses propres données :

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

Sortie :

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

Défi

Facile

Déclarez les classes d’erreur InvalidAmount et InsufficientFunds, qui sont toutes deux des sous-classes de StandardError, et complétez Account. deposit(amount) et withdraw(amount) lèvent InvalidAmount avec amount must be positive pour les montants inférieurs à 1. withdraw lève InsufficientFunds avec balance is only 20 lorsque le montant est supérieur au solde. Rien ne change lorsqu’une erreur est levée.

Le code fourni lit les commandes dep n ou wd n et affiche balance 70 après chacune d’elles, invalid: amount must be positive pour InvalidAmount et refused: balance is only 20 pour InsufficientFunds.

Votre code doit être placé dans invalid_amount.rb, insufficient_funds.rb et account.rb. main.rb contient le code fourni pour les entrées et sorties et ne peut pas être modifié.

Essayez vous-même

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

# Code d'entrée/sortie fourni : le garder tel quel
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 iconTestez-vous

Cette leçon comprend un petit quiz. Commencez la leçon pour y répondre et suivre votre progression.

Toutes les leçons de Programmation orientée objet

Entraînez-vous par vous-même : Compilateur Ruby en ligne