Menu
Coddy logo textTech

Metody chronione

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

Metoda chroniona znajduje się pomiędzy publiczną a prywatną. Kod spoza klasy nie może jej wywołać, ale każdy obiekt tej samej klasy może wywołać ją na innym obiekcie. Dzięki temu dwa obiekty mogą porównywać dane, których nie ujawniają reszcie programu:

class Account
  def initialize(owner, balance)
    @owner = owner
    @balance = balance
  end

  def richer_than?(other)
    balance > other.balance
  end

  protected

  def balance
    @balance
  end
end

ada = Account.new("Ada", 500)
bo = Account.new("Bo", 300)
puts ada.richer_than?(bo)
begin
  ada.balance
rescue NoMethodError => e
  puts e.message
end

Wynik:

true
protected method `balance' called for an instance of Account

Różnica w porównaniu z private ujawnia się, gdy odbiorcą jest inny obiekt. other.balance działa w przypadku metody chronionej, a w przypadku prywatnej — nie:

class Sample
  def initialize(n)
    @n = n
  end

  def compare_protected(other)
    other.guarded + guarded
  end

  def compare_private(other)
    other.hidden + hidden
  rescue NoMethodError
    "private refused other.hidden"
  end

  protected

  def guarded
    @n
  end

  private

  def hidden
    @n
  end
end

a = Sample.new(1)
b = Sample.new(2)
puts a.compare_protected(b)
puts a.compare_private(b)

Wynik:

3
private refused other.hidden

protected działa również z attr_reader, czyli typowym sposobem udostępniania wartości tylko obiektom jednej klasy, na przykład po to, by zdecydować, który z dwóch obiektów wygrywa:

class Player
  attr_reader :name

  def initialize(name, rating)
    @name = name
    @rating = rating
  end

  def stronger?(other)
    rating > other.rating
  end

  protected

  attr_reader :rating
end

a = Player.new("Ada", 1200)
b = Player.new("Bo", 1100)
puts a.stronger?(b)
puts [a, b].select { |pl| pl.stronger?(b) }.map(&:name).inspect

Wynik:

true
["Ada"]

Wybierz najwęższy poziom, który działa. public dotyczy interfejsu klasy, protected danych, które obiekty tej klasy porównują ze sobą, a private wszystkiego innego:

public      # każdy: order.total
protected   # obiekty danej klasy: other.balance wewnątrz Account
private     # tylko sam obiekt: subtotal, self.subtotal
challenge icon

Wyzwanie

Łatwy

Uzupełnij klasę Account. Saldo może być odczytywane tylko przez inne konta: ustaw jego czytnik jako chroniony. richer_than?(other) porównuje salda, a transfer_to(other, amount) przekazuje pieniądze na inne konto za pomocą chronionej metody tego konta receive(amount) i zwraca true albo zwraca false, gdy saldo jest za niskie. to_s zwraca tylko imię właściciela.

Dostarczony kod odczytuje imiona i salda dwóch właścicieli, a następnie polecenia transfer a b 50, wypisując ok lub refused. Następnie wypisuje Ada is richer (albo Bo is richer, albo equal), a na końcu próbuje odczytać balance z zewnątrz, wypisując balance is protected, gdy Ruby odmówi.

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

Spróbuj swoich sił

require_relative 'account'

# Dostarczony kod wejścia/wyjścia: pozostaw go bez zmian
input = []
while (line = gets)
  input << line.chomp
end
accounts = input[0..1].map do |line|
  owner, balance = line.split
  Account.new(owner, balance.to_i)
end
a, b = accounts
input.drop(2).each do |line|
  _, from, to, amount = line.split
  source = from == "a" ? a : b
  target = to == "a" ? a : b
  puts source.transfer_to(target, amount.to_i) ? "ok" : "refused"
end
if a.richer_than?(b) then puts "#{a} is richer"
elsif b.richer_than?(a) then puts "#{b} is richer"
else puts "equal"
end
begin
  a.balance
  puts "balance is public"
rescue NoMethodError
  puts "balance is protected"
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