Menu
Coddy logo textTech

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 le lecteur 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

Sortie :

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

Un setter 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

Sortie :

true

Passer par un setter privé plutôt que d’affecter directement @balance permet de centraliser les règles, comme la vérification que la valeur est valide :

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

Sortie :

50
0

private attr_accessor :history masque à la fois le lecteur et le setter, 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 est lisible depuis l’extérieur, mais ne peut être défini que par l’intermédiaire d’un setter privé, en commençant à 0. earn(amount) l’incrémente. spend(amount) le décrémente et renvoie true, ou renvoie false sans effectuer de modification lorsque le solde est insuffisant. history renvoie le nombre de modifications réussies, également protégé par un setter privé.

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

Votre code doit être placé dans wallet.rb. main.rb contient le code fourni de lecture des entrées et d’affichage des sorties et ne peut pas être modifié.

Essayez vous-même

require_relative 'wallet'

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