Menu
Coddy logo textTech

Prywatne settery

Część sekcji Programowanie obiektowe ścieżki Ruby w Coddy. Lekcja 19 z 57.

Często wartość powinna być dostępna do odczytu dla wszystkich, ale zmieniana tylko przez obiekt. attr_reader udostępnia publiczny odczyt, a private attr_writer — metodę ustawiającą, z której może korzystać tylko obiekt. Działa to dlatego, że attr_writer zwraca nazwy metod, które definiuje:

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

Wynik:

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

Prywatny setter jest wywoływany za pomocą self. wewnątrz klasy — to jedyny odbiorca, którego Ruby dopuszcza w przypadku metod prywatnych. Bez self. przypisanie utworzyłoby jedynie zmienną lokalną:

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

Wynik:

true

Korzystanie z prywatnego settera zamiast bezpośredniego przypisywania wartości do @balance pozwala umieścić reguły w jednym miejscu, na przykład sprawdzenie, czy wartość jest prawidłowa:

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

Wynik:

50
0

private attr_accessor :history ukrywa zarówno metodę odczytującą, jak i ustawiającą, w przypadku stanu wewnętrznego, którego kod zewnętrzny nie powinien nawet widzieć:

class Editor
  def initialize
    self.history = []
  end

  def type(text)
    history << text
  end

  private attr_accessor :history
end
challenge icon

Wyzwanie

Łatwy

Uzupełnij klasę Wallet. balance można odczytać z zewnątrz, ale ustawiać tylko za pomocą prywatnego settera; jego początkowa wartość to 0. earn(amount) dodaje do niego podaną wartość. spend(amount) odejmuje podaną wartość i zwraca true albo zwraca false bez wprowadzania zmian, gdy saldo jest zbyt niskie. history zwraca liczbę udanych zmian i również jest chronione przez prywatny setter.

Dostarczony kod odczytuje polecenia +n lub -n, po każdym wypisując balance 30, a gdy operacja wydania środków się nie powiedzie — declined. Następnie wypisuje changes: 2 i próbuje wykonać wallet.balance = 1000000, wypisując cannot set balance, gdy Ruby odrzuci tę próbę.

Twój kod umieść w pliku wallet.rb. Plik main.rb zawiera dostarczony kod wejścia/wyjścia i nie można go edytować.

Spróbuj swoich sił

require_relative 'wallet'

# Dostarczony kod wejścia/wyjścia: pozostaw go bez zmian
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 iconSprawdź się

Ta lekcja zawiera krótki quiz. Zacznij lekcję, żeby na niego odpowiedzieć i śledzić swoje postępy.

Wszystkie lekcje w sekcji Programowanie obiektowe

Poćwicz samodzielnie: Kompilator Ruby online