Menu
CoddyTech

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 mettraient son état en péril. raise arrête la méthode avant que quoi que ce soit ne change. Le nom de votre propre classe d’erreur désigne 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 déclenchent :

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 reste possible. 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

Résultat :

true
FrozenError
1

Un lecteur qui renvoie un tableau interne transmet le véritable tableau, que du code externe pourrait donc modifier. Renvoyez une copie gelée avec @items.dup.freeze, ou @items.dup, pour 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éclare les classes d’erreur InvalidAmount et InsufficientFunds, toutes deux sous-classes de StandardError, et complète 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 des commandes dep n ou wd n et affiche balance 70 après chacune, invalid: amount must be positive pour InvalidAmount et refused: balance is only 20 pour InsufficientFunds.

Ton code doit être placé dans invalid_amount.rb, insufficient_funds.rb et account.rb. main.rb contient le code d’entrée/sortie fourni 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 : conservez-le 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