Menu
Coddy logo textTech

Защищённые методы

Часть раздела Объектно-ориентированное программирование путешествия по Ruby на Coddy. Урок 18 из 57.

Метод protected занимает промежуточное положение между public и private. Код за пределами класса не может его вызвать, но любой объект того же класса может вызвать его у другого объекта. Это позволяет двум объектам сравнивать данные, которые они не показывают остальной части программы:

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

Вывод:

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

Разница с private проявляется, когда получателем является другой объект. other.balance работает для защищённого метода и не работает для приватного:

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)

Вывод:

3
private refused other.hidden

protected также работает с attr_reader — это обычный способ сделать значение доступным только объектам одного класса, например, чтобы определить, какой из двух объектов побеждает:

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

Вывод:

true
["Ada"]

Выбирайте самый узкий подходящий уровень. public — для интерфейса класса, protected — для данных, с которыми объекты класса сравниваются друг с другом, а private — для всего остального:

public      # кто угодно: order.total
protected   # объекты класса: other.balance внутри Account
private     # только сам объект: subtotal, self.subtotal
challenge icon

Задание

Легко

Завершите класс Account. Баланс должен быть доступен для чтения только другим аккаунтам: сделайте его средство чтения защищённым. richer_than?(other) сравнивает балансы, transfer_to(other, amount) переводит деньги на другой аккаунт через защищённый метод другого аккаунта receive(amount) и возвращает true либо возвращает false, если баланс слишком мал. to_s возвращает только имя владельца.

Предоставленный код считывает имена двух владельцев и их балансы, затем команды transfer a b 50, выводя ok или refused. Затем он выводит Ada is richer (или Bo is richer, или equal) и наконец пытается прочитать balance извне, выводя balance is protected, когда Ruby отказывает в доступе.

Ваш код должен находиться в account.rb. В main.rb содержится предоставленный код ввода/вывода, и его нельзя изменять.

Попробуйте сами

require_relative 'account'

# Предоставленный код ввода/вывода: оставьте его как есть
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 iconПроверьте себя

В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.

Все уроки раздела Объектно-ориентированное программирование

Потренируйтесь самостоятельно: Онлайн-компилятор Ruby