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.balanceRésultat :
InsufficientFunds: only 50 available
50Des 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}"
endSortie :
charged 40
fix the input: amount must be positive
try later: limit is 100freeze 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.xSortie :
true
FrozenError
1Un 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.songsSortie :
songs are read-only
["Intro"]Défi
FacileDé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
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
1Classes et objets
Travailler avec des fichiersDéfinir des classesVariables d’instanceMéthodes et selfto_s et inspectRécapitulatif - Carte de bibliothèque4Encapsulation
Méthodes privéesMéthodes protégéesSetters privésProtéger l’étatRécapitulatif - Distributeur automatique7Polymorphisme et typage canard
PolymorphismeTypage canardMéthodes abstraitesCas et classesRécapitulatif - méthodes de paiement10Patrons de conception – Partie 2
Délégation avec ForwardablePatron DécorateurPatron BuilderPatron État2Accesseurs et self
Lecteurs et écrivainsSetters personnalisésself dans les méthodesIdentité et égalitéRécapitulatif - Liste de contacts5Héritage
Sous-classesRedéfinition des méthodesAppeler superis_a? et ancêtresRécapitulatif - Paie du personnel8Méthodes spéciales
Égalité et hachageMéthodes d’opérateursIndexation et ajoutCollections personnaliséesRécapitulatif - Fractions11Projet : Gestion de bibliothèque
Livres et membresEmprunter des livresEntraînez-vous par vous-même : Compilateur Ruby en ligne