Menu
Coddy logo textTech

Private Setter

Teil des Abschnitts Objektorientierte Programmierung der Ruby-Journey von Coddy. Lektion 19 von 57.

Oft sollte ein Wert für alle lesbar, aber nur vom Objekt veränderbar sein. attr_reader stellt den öffentlichen Leser bereit, und private attr_writer einen Setter, den nur das Objekt verwenden kann. Das funktioniert, weil attr_writer die Namen der von ihm definierten Methoden zurückgibt:

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

Ausgabe:

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

Ein privater Setter wird innerhalb der Klasse mit self. aufgerufen, dem einzigen Empfänger, den Ruby für private Methoden akzeptiert. Ohne self. würde die Zuweisung lediglich eine lokale Variable erstellen:

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

Ausgabe:

true

Wenn du einen privaten Setter verwendest, statt @balance direkt zuzuweisen, gibt es eine zentrale Stelle für Regeln, etwa eine Prüfung, ob der Wert gültig ist:

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

Ausgabe:

50
0

private attr_accessor :history verbirgt sowohl den Leser als auch den Setter für einen internen Zustand, den Code außerhalb der Klasse nicht einmal sehen sollte:

class Editor
  def initialize
    self.history = []
  end

  def type(text)
    history << text
  end

  private attr_accessor :history
end
challenge icon

Aufgabe

Einfach

Vervollständige die Klasse Wallet. balance ist von außen lesbar, kann aber nur über einen privaten Setter gesetzt werden und beginnt bei 0. earn(amount) addiert einen Wert dazu. spend(amount) zieht einen Wert ab und gibt true zurück oder gibt false zurück, ohne etwas zu ändern, wenn das Guthaben zu niedrig ist. history gibt die Anzahl der erfolgreichen Änderungen zurück und wird ebenfalls hinter einem privaten Setter gehalten.

Der bereitgestellte Code liest Befehle +n oder -n ein und gibt nach jedem Befehl balance 30 aus oder declined, wenn ein Ausgeben fehlschlägt. Anschließend gibt er changes: 2 aus und versucht wallet.balance = 1000000, wobei cannot set balance ausgegeben wird, wenn Ruby dies verweigert.

Dein Code gehört in wallet.rb. main.rb enthält den bereitgestellten Eingabe-/Ausgabecode und darf nicht bearbeitet werden.

Probier es selbst

require_relative 'wallet'

# Mitgelieferter Ein-/Ausgabecode: belasse ihn wie er ist
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 iconTeste dich selbst

Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.

Alle Lektionen in Objektorientierte Programmierung

Übe selbstständig: Online-Ruby-Compiler