Menu
CoddyTech

Setters privés

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

Souvent, une valeur devrait être lisible par tout le monde, mais modifiable uniquement par l’objet. attr_reader fournit l’accesseur public, et private attr_writer un accesseur en écriture que seul l’objet peut utiliser. Cela fonctionne parce que attr_writer renvoie les noms des méthodes qu’il définit :

class Wallet
  attr_reader :balance

  def initialize
    self.balance = 0
  end

  def earn(amount)
    self.balance = balance + amount
  end

  private attr_writer :balance
end

w = Wallet.new
w.earn(30)
puts w.balance
begin
  w.balance = 1_000_000
rescue NoMethodError => e
  puts e.message
end

Résultat :

30
private method `balance=' called for an instance of Wallet

Un accesseur privé est appelé avec self. à l’intérieur de la classe, le seul récepteur que Ruby accepte pour les méthodes privées. Sans self., l’affectation ne ferait que créer une variable locale :

class Lamp
  attr_reader :on

  def initialize
    self.on = false
  end

  def toggle
    self.on = !on
    self
  end

  private

  attr_writer :on
end

puts Lamp.new.toggle.toggle.toggle.on

Résultat :

true

Passer par un accesseur en écriture privé plutôt que d’assigner directement @balance permet de centraliser les règles, comme la vérification de la validité de la valeur :

class Tank
  attr_reader :litres

  def initialize(capacity)
    @capacity = capacity
    self.litres = 0
  end

  def fill(amount)
    self.litres = litres + amount
  end

  def drain(amount)
    self.litres = litres - amount
  end

  private

  def litres=(value)
    @litres = value.clamp(0, @capacity)
  end
end

t = Tank.new(50)
t.fill(80)
puts t.litres
t.drain(70)
puts t.litres

Résultat :

50
0

private attr_accessor :history masque à la fois le lecteur et le mutateur, pour un état interne que le code externe ne devrait même pas voir :

class Editor
  def initialize
    self.history = []
  end

  def type(text)
    history << text
  end

  private attr_accessor :history
end
challenge icon

Défi

Facile

Complétez la classe Wallet. balance peut être lu depuis l’extérieur, mais ne peut être défini qu’au moyen d’un accesseur privé ; sa valeur initiale est 0. earn(amount) l’augmente. spend(amount) le diminue et renvoie true, ou renvoie false sans effectuer de changement lorsque le solde est insuffisant. history renvoie le nombre de changements réussis et est également protégé par un accesseur privé.

Le code fourni lit les commandes +n ou -n, puis affiche balance 30 après chacune, ou declined lorsqu’une dépense échoue. Ensuite, il affiche changes: 2 et essaie wallet.balance = 1000000, puis affiche cannot set balance lorsque Ruby refuse.

Écrivez votre code dans wallet.rb. main.rb contient le code fourni pour les entrées/sorties et ne peut pas être modifié.

Essayez vous-même

require_relative 'wallet'

# Code d'entrée/sortie fourni : conservez-le tel quel
input = []
while (line = gets)
  input << line.chomp
end
wallet = Wallet.new
input.each do |cmd|
  amount = cmd[1..].to_i
  if cmd.start_with?("+")
    wallet.earn(amount)
    puts "balance #{wallet.balance}"
  elsif wallet.spend(amount)
    puts "balance #{wallet.balance}"
  else
    puts "declined"
  end
end
puts "changes: #{wallet.history}"
begin
  wallet.balance = 1_000_000
  puts "balance set"
rescue NoMethodError
  puts "cannot set balance"
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