Menu
Coddy logo textTech

Delegowanie z Forwardable

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

Delegowanie oznacza, że obiekt przekazuje część swojej pracy obiektowi, który zawiera, zamiast po nim dziedziczyć. Playlista zawierająca tablicę utworów jest bezpieczniejsza niż taka, która sama jest tablicą, ponieważ kod zewnętrzny uzyskuje dostęp tylko do metod, które zdecydujesz się przekazać dalej:

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

Wynik:

2
Intro

Ręczne pisanie każdej metody przekazującej wywołanie jest powtarzalne. Biblioteka standardowa robi to za pomocą Forwardable: extend Forwardable, a następnie def_delegators :@songs, :size, :first przekazuje te wywołania do obiektu w @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?

Wynik:

2 Intro Dune
true
false

def_delegator :@songs, :push, :add przekazuje jedną metodę pod nową nazwą. Obiekt zachowuje pełną kontrolę nad wszystkim, czego nie przekazuje, na przykład nad delete lub 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)

Wynik:

a
1
false

Wybierz delegowanie („ma”) wtedy, gdy klasa potrzebuje tylko części zachowania innej klasy. Dziedziczenie po Array udostępniłoby dziesiątki metod, takich jak clear i delete_at, które mogłyby naruszyć zasady playlisty:

class Playlist < Array      # każda metoda Array jest publiczna, w tym clear
end

class Playlist              # publiczne jest tylko to, co delegujesz
  extend Forwardable
  def_delegators :@songs, :size, :each
end
challenge icon

Wyzwanie

Łatwy

Uzupełnij klasę Playlist za pomocą Forwardable. Przechowuje ona utwory w @songs, każdy jako parę [title, seconds]. Przekaż metody size, empty? i first za pomocą def_delegators, a metodę push pod nazwą add za pomocą def_delegator. Jej własna metoda duration zwraca łączny czas trwania w formacie m:ss, a metoda titles zwraca tytuły połączone za pomocą , .

Dostarczony kod dodaje jeden utwór dla każdej linii wejściowej (title,seconds) za pomocą add, a następnie wypisuje rozmiar, pierwszy tytuł, tytuły, czas trwania oraz informację, czy playlista odpowiada na metodę clear.

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

Spróbuj swoich sił

require_relative 'playlist'

# Dostarczony kod wejścia/wyjścia: pozostaw go bez zmian
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 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