Menu
Coddy logo textTech

Métodos protegidos

Parte da seção Programação orientada a objetos do Journey de Ruby da Coddy. Lição 18 de 57.

Um método protected fica entre público e privado. Código externo não pode chamá-lo, mas qualquer objeto da mesma classe pode chamá-lo em outro objeto. Isso permite que dois objetos comparem dados que não mostram ao restante do programa:

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

Saída:

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

A diferença em relação a private aparece quando o receptor é outro objeto. other.balance funciona para um método protegido e falha para um método privado:

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)

Saída:

3
private refused other.hidden

protected também funciona com attr_reader, a forma usual de compartilhar um valor apenas entre objetos de uma classe, por exemplo, para decidir qual de dois objetos vence:

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

Saída:

true
["Ada"]

Escolha o nível mais restrito que funcione. public é para a interface da classe, protected para dados com os quais os objetos da classe se comparam, e private para todo o restante:

public      # qualquer um: order.total
protected   # objetos da classe: other.balance dentro de Account
private     # apenas o próprio objeto: subtotal, self.subtotal
challenge icon

Desafio

Fácil

Complete a classe Account. O saldo pode ser lido apenas por outras contas: torne seu leitor protegido. richer_than?(other) compara os saldos, transfer_to(other, amount) transfere dinheiro para outra conta por meio do método protegido receive(amount) da outra conta e retorna true, ou retorna false quando o saldo é insuficiente. to_s retorna apenas o nome do proprietário.

O código fornecido lê os dois proprietários e saldos, depois os comandos transfer a b 50, exibindo ok ou refused. Em seguida, exibe Ada is richer (ou Bo is richer, ou equal) e, por fim, tenta ler balance de fora, exibindo balance is protected quando o Ruby recusa.

Seu código deve ficar em account.rb. main.rb contém o código fornecido de entrada/saída e não pode ser editado.

Experimente você mesmo

require_relative 'account'

# Código de entrada/saída fornecido: mantenha-o como está
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 iconTeste seus conhecimentos

Esta lição inclui um quiz rápido. Comece a lição para respondê-lo e acompanhar seu progresso.

Todas as lições de Programação orientada a objetos

Pratique por conta própria: Compilador de Ruby online