Menu
Coddy logo textTech

Delegação com Forwardable

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

Delegação significa que um objeto transfere parte de seu trabalho para um objeto que ele contém, em vez de herdar dele. Uma playlist que contém um array de músicas é mais segura do que uma que é um array, porque o código externo só obtém os métodos que você escolhe encaminhar:

class Playlist
  def initialize
    @songs = []
  end

  def add(song)
    @songs << song
    self
  end

  def size
    @songs.size
  end

  def first
    @songs.first
  end
end

list = Playlist.new.add("Intro").add("Dune")
puts list.size
puts list.first

Saída:

2
Intro

Escrever cada método de encaminhamento manualmente é repetitivo. A biblioteca padrão Forwardable faz isso: extend Forwardable e, em seguida, def_delegators :@songs, :size, :first encaminha essas chamadas para o objeto em @songs:

require 'forwardable'

class Playlist
  extend Forwardable
  def_delegators :@songs, :size, :first, :last, :empty?, :include?

  def initialize
    @songs = []
  end

  def add(song)
    @songs << song
    self
  end
end

list = Playlist.new.add("Intro").add("Dune")
puts "#{list.size} #{list.first} #{list.last}"
puts list.include?("Dune")
puts list.empty?

Saída:

2 Intro Dune
true
false

def_delegator :@songs, :push, :add encaminha um método com um novo nome. O objeto mantém controle total sobre tudo o que não encaminha, por exemplo delete ou clear:

require 'forwardable'

class Queue2
  extend Forwardable
  def_delegator :@jobs, :push, :enqueue
  def_delegator :@jobs, :shift, :next_job
  def_delegators :@jobs, :size

  def initialize
    @jobs = []
  end
end

q = Queue2.new
q.enqueue("a")
q.enqueue("b")
puts q.next_job
puts q.size
puts q.respond_to?(:clear)

Saída:

a
1
false

Prefira a delegação ("tem um") quando uma classe precisar apenas de parte do comportamento de outra classe. Herdar de Array exporia dezenas de métodos, como clear e delete_at, que poderiam violar as regras da playlist:

class Playlist < Array      # todos os métodos de Array são públicos, incluindo clear
end

class Playlist              # apenas o que você encaminha é público
  extend Forwardable
  def_delegators :@songs, :size, :each
end
challenge icon

Desafio

Fácil

Complete a classe Playlist com Forwardable. Ela armazena músicas, cada uma um par [title, seconds], em @songs. Encaminhe size, empty? e first com def_delegators e encaminhe push com o nome add usando def_delegator. Seu próprio método duration retorna a duração total como m:ss, e titles retorna os títulos unidos por , .

O código fornecido adiciona uma música por linha de entrada (title,seconds) com add e depois imprime o tamanho, o primeiro título, os títulos, a duração e se a playlist responde a clear.

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

Experimente você mesmo

require_relative 'playlist'

# Código de entrada/saída fornecido: mantenha-o como está
input = []
while (line = gets)
  input << line.chomp
end
list = Playlist.new
input.each do |line|
  title, seconds = line.split(",")
  list.add([title, seconds.to_i])
end
puts "songs: #{list.size}"
puts "first: #{list.empty? ? "none" : list.first[0]}"
puts "titles: #{list.titles}"
puts "duration: #{list.duration}"
puts "can clear: #{list.respond_to?(:clear)}"
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