Menu
CoddyTech

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

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

Метод protected занимает промежуточное положение между публичным и приватным. Внешний код не может его вызвать, но любой объект того же класса может вызвать его для другого объекта. Благодаря этому два объекта могут сравнивать данные, которые они не показывают остальной части программы:

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