Делегирование с помощью Forwardable
Часть раздела Объектно-ориентированное программирование путешествия по Ruby на Coddy. Урок 46 из 57.
Делегирование означает, что объект передаёт часть своей работы объекту, который он содержит, вместо того чтобы наследовать от него. Плейлист, содержащий массив песен, безопаснее того, который сам является массивом, потому что внешний код получает доступ только к тем методам, которые вы решите перенаправить:
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Вывод:
2
IntroПисать каждый метод переадресации вручную утомительно. Для этого есть стандартная библиотека Forwardable: extend Forwardable, затем def_delegators :@songs, :size, :first переадресует эти вызовы объекту в @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?Вывод:
2 Intro Dune
true
falsedef_delegator :@songs, :push, :add перенаправляет один метод под новым именем. Объект сохраняет полный контроль над всем, что он не перенаправляет, например над delete или 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)Вывод:
a
1
falseПредпочитай делегирование («имеет»), когда классу нужна лишь часть поведения другого класса. Наследование от Array открыло бы десятки методов, например clear и delete_at, которые могли бы нарушить правила плейлиста:
class Playlist < Array # все методы Array общедоступны, включая clear
end
class Playlist # общедоступно только то, что вы делегируете
extend Forwardable
def_delegators :@songs, :size, :each
endЗадание
ЛегкоДополните класс Playlist с помощью Forwardable. Он хранит песни в @songs, каждая песня — это пара [title, seconds]. Перенаправьте вызовы size, empty? и first с помощью def_delegators, а вызовы push перенаправьте под именем add с помощью def_delegator. Собственный метод duration должен возвращать общую продолжительность в формате m:ss, а titles — названия, объединённые с помощью , .
Предоставленный код добавляет песню для каждой строки ввода (title,seconds) с помощью add, а затем выводит количество песен, название первой песни, названия песен, продолжительность и информацию о том, отвечает ли плейлист на вызов clear.
Ваш код должен находиться в playlist.rb. В main.rb находится предоставленный код ввода/вывода, который нельзя редактировать.
Попробуйте сами
require_relative 'playlist'
# Предоставленный код ввода-вывода: оставьте его без изменений
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)}"
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Объектно-ориентированное программирование
1Классы и объекты
Работа с файламиОпределение классовПеременные экземпляраМетоды и selfto_s и inspectПовторение — читательский билет4Инкапсуляция
Приватные методыЗащищённые методыПриватные сеттерыЗащита состоянияПовторение — торговый автомат7Полиморфизм и утиная типизация
ПолиморфизмУтиная типизацияАбстрактные методыcase и классыПовторение — способы оплаты10Шаблоны проектирования, часть 2
Делегирование с помощью ForwardableШаблон декоратораШаблон строителяШаблон состояния2Аксессоры и self
Чтение и записьПользовательские сеттерыself в методахИдентичность и равенствоПовторение — список контактов5Наследование
ПодклассыПереопределение методовВызов superis_a? и предкиПовторение — расчёт зарплаты сотрудников8Специальные методы
Равенство и хешированиеМетоды операторовИндексация и добавлениеПользовательские коллекцииПовторение — дроби11Проект: Управление библиотекой
Книги и читателиВыдача книг3Методы и переменные классов
Методы классовПеременные экземпляра классаКонстантыФабричные методыПовторение — коды купонов6Модули и примеси
Модули и includeРасширение объектовМодуль ComparableМодуль EnumerableПовторение — спортивная лига9Паттерны проектирования. Часть 1
Паттерн «Одиночка»Фабричный паттернПаттерн «Наблюдатель»Паттерн «Стратегия»Потренируйтесь самостоятельно: Онлайн-компилятор Ruby