Шаблон декоратора
Часть раздела Объектно-ориентированное программирование путешествия по Ruby на Coddy. Урок 47 из 57.
Паттерн декоратор оборачивает объект в другой объект с тем же интерфейсом, который добавляет что-либо до или после передачи вызова дальше. Декораторы можно комбинировать в любом порядке:
class Coffee
def cost
2.0
end
def description
"coffee"
end
end
class Milk
def initialize(drink)
@drink = drink
end
def cost
@drink.cost + 0.5
end
def description
"#{@drink.description}, milk"
end
end
order = Milk.new(Milk.new(Coffee.new))
puts format("%s: %.2f", order.description, order.cost)Вывод:
coffee, milk, milk: 3.00Каждый декоратор сохраняет обёрнутый объект и передаёт вызовы ему, поэтому общий базовый класс может содержать эту часть. Затем каждый конкретный декоратор переопределяет только то, что он изменяет:
class Coffee
def cost
2.0
end
def description
"coffee"
end
end
class Extra
def initialize(drink)
@drink = drink
end
def cost
@drink.cost
end
def description
@drink.description
end
end
class Sugar < Extra
def cost
super + 0.2
end
def description
super + ", sugar"
end
end
order = Sugar.new(Coffee.new)
puts format("%s: %.2f", order.description, order.cost)Вывод:
coffee, sugar: 2.20Стандартная библиотека SimpleDelegator (require 'delegate') перенаправляет каждый метод, который она не определяет, обёрнутому объекту, благодаря чему декораторы остаются короткими даже при большом интерфейсе:
require 'delegate'
class Loud < SimpleDelegator
def to_s
super.upcase + "!"
end
end
text = Loud.new("hello")
puts text
puts text.size
puts text.start_with?("he")Вывод:
HELLO!
5
trueДекораторы добавляют поведение отдельным объектам во время выполнения и могут свободно комбинироваться. При использовании подклассов для каждой комбинации, например MilkSugarCoffee, потребовался бы отдельный класс:
Sugar.new(Milk.new(Coffee.new)) # любая комбинация, любой порядок
Whip.new(Sugar.new(Coffee.new))
# с подклассами: MilkCoffee, SugarCoffee, MilkSugarCoffee, ...Задание
ЛегкоЗавершите декораторы кофе. Coffee стоит 2.00 и описывается как coffee. Milk добавляет 0.50 и , milk, Sugar добавляет 0.20 и , sugar, а Whip добавляет 0.70 и , whip. Три декоратора используют общий базовый класс Extra, который хранит обёрнутый напиток и передаёт вызовы cost и description; каждый подкласс использует super.
Каждая входная строка представляет собой заказ, например milk sugar. Предоставленный код оборачивает новый объект Coffee в декораторы в указанном порядке и выводит coffee, milk, sugar: 2.70.
Ваш код должен находиться в файлах coffee.rb, extra.rb, milk.rb, sugar.rb и whip.rb. В main.rb находится предоставленный код ввода/вывода, и его нельзя редактировать.
Попробуйте сами
require_relative 'coffee'
require_relative 'extra'
require_relative 'milk'
require_relative 'sugar'
require_relative 'whip'
# Предоставленный код ввода/вывода: оставьте его как есть
input = []
while (line = gets)
input << line.chomp
end
extras = { "milk" => Milk, "sugar" => Sugar, "whip" => Whip }
input.each do |line|
drink = line.split.reduce(Coffee.new) { |d, name| extras[name].new(d) }
puts format("%s: %.2f", drink.description, drink.cost)
end
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Объектно-ориентированное программирование
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