Menu
Coddy logo textTech

Delegation mit Forwardable

Teil des Abschnitts Objektorientierte Programmierung der Ruby-Journey von Coddy. Lektion 46 von 57.

Delegation bedeutet, dass ein Objekt einen Teil seiner Arbeit an ein Objekt übergibt, das es enthält, anstatt von ihm zu erben. Eine Playlist, die ein Array von Songs enthält, ist sicherer als eine, die selbst ein Array ist, da externer Code nur Zugriff auf die Methoden erhält, die du weiterleitest:

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

Ausgabe:

2
Intro

Jede Weiterleitungsmethode von Hand zu schreiben, ist repetitiv. Die Standardbibliothek übernimmt das mit Forwardable: extend Forwardable, anschließend leitet def_delegators :@songs, :size, :first diese Aufrufe an das Objekt in @songs weiter:

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?

Ausgabe:

2 Intro Dune
true
false

def_delegator :@songs, :push, :add leitet eine Methode unter einem neuen Namen weiter. Das Objekt behält die vollständige Kontrolle über alles, was es nicht weiterleitet, zum Beispiel delete oder 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)

Ausgabe:

a
1
false

Bevorzuge Delegation („has a“), wenn eine Klasse nur einen Teil des Verhaltens einer anderen Klasse benötigt. Von Array zu erben würde Dutzende von Methoden verfügbar machen, etwa clear und delete_at, die die Regeln der Playlist verletzen könnten:

class Playlist < Array      # jede Array-Methode ist öffentlich, einschließlich clear
end

class Playlist              # nur was du weiterleitest ist öffentlich
  extend Forwardable
  def_delegators :@songs, :size, :each
end
challenge icon

Aufgabe

Einfach

Vervollständige die Klasse Playlist mit Forwardable. Sie enthält Lieder, jeweils ein [title, seconds]-Paar, in @songs. Leite size, empty? und first mit def_delegators weiter und leite push unter dem Namen add mit def_delegator weiter. Ihre eigene Methode duration gibt die Gesamtlänge als m:ss zurück, und titles gibt die durch , verbundenen Titel zurück.

Der bereitgestellte Code fügt pro Eingabezeile (title,seconds) mit add ein Lied hinzu und gibt anschließend die Größe, den ersten Titel, die Titel, die Dauer und aus, ob die Playlist auf clear reagiert.

Dein Code gehört in playlist.rb. main.rb enthält den bereitgestellten Ein-/Ausgabecode und darf nicht bearbeitet werden.

Probier es selbst

require_relative 'playlist'

# Bereitgestellter Ein-/Ausgabecode: belasse ihn wie er ist
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 dich selbst

Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.

Alle Lektionen in Objektorientierte Programmierung

Übe selbstständig: Online-Ruby-Compiler