Паттерн «Стратегия»
Часть раздела Объектно-ориентированное программирование путешествия по Ruby на Coddy. Урок 45 из 57.
Паттерн стратегия помещает одну часть алгоритма в отдельный объект и позволяет основному объекту делегировать ему выполнение этой части. Замена стратегии изменяет поведение, не изменяя основной класс:
class Standard
def cost(kg)
5 + kg * 1.5
end
end
class Express
def cost(kg)
15 + kg * 3
end
end
class Shipment
attr_writer :strategy
def initialize(kg, strategy)
@kg = kg
@strategy = strategy
end
def cost
@strategy.cost(@kg)
end
end
s = Shipment.new(2, Standard.new)
puts s.cost
s.strategy = Express.new
puts s.costВывод:
8.0
21Стратегии используют только одно имя метода, поэтому работает любой объект с cost: снова утиная типизация. Добавление нового способа доставки означает добавление одного класса, а не ещё одной ветви внутри Shipment:
class Pickup
def cost(_kg)
0
end
end
shipment.strategy = Pickup.new # Shipment не изменёнВ Ruby стратегией часто является лямбда. Всё, что реагирует на call, можно сохранить и вызвать — будь то лямбда, объект метода или объект собственного класса:
class TextFormatter
def initialize(strategy)
@strategy = strategy
end
def format_all(words)
words.map { |w| @strategy.call(w) }.join(" ")
end
end
class Censor
def call(word)
word.size > 3 ? "*" * word.size : word
end
end
words = %w[ruby is great]
puts TextFormatter.new(->(w) { w.upcase }).format_all(words)
puts TextFormatter.new(:capitalize.to_proc).format_all(words)
puts TextFormatter.new(Censor.new).format_all(words)Результат:
RUBY IS GREAT
Ruby Is Great
**** is *****Выбирайте стратегию вместо подклассов, когда один объект должен изменять своё поведение во время работы программы или когда вариация представляет собой один шаг. Подкласс фиксирует поведение на весь срок существования объекта:
# подкласси: фиксируется при создании
ExpressShipment.new(2)
# стратегия: можно изменить позже
shipment = Shipment.new(2, Standard.new)
shipment.strategy = Express.newЗадание
ЛегкоЗавершите стратегии доставки и ShippingCalculator. Standard#cost(kg) равен 5 плюс 1.50 за кг, Express#cost(kg) равен 15 плюс 3 за кг, а Pickup#cost(kg) равен 0. Калькулятор создаётся со стратегией, её можно изменить с помощью strategy=, а quote(kg) возвращает 2.5 kg by Express: 22.50, используя имя класса стратегии.
Предоставленный код начинает работу со Standard и считывает команды use express, use pickup, use standard или quote 2.5, выводя каждую оценку стоимости.
Ваш код должен находиться в файлах standard.rb, express.rb, pickup.rb и shipping_calculator.rb. В main.rb содержится предоставленный код ввода/вывода, и его нельзя изменять.
Попробуйте сами
require_relative 'standard'
require_relative 'express'
require_relative 'pickup'
require_relative 'shipping_calculator'
# Предоставленный код ввода/вывода: оставьте его как есть
input = []
while (line = gets)
input << line.chomp
end
strategies = { "standard" => Standard, "express" => Express, "pickup" => Pickup }
calc = ShippingCalculator.new(Standard.new)
input.each do |line|
cmd, arg = line.split
if cmd == "use"
calc.strategy = strategies[arg].new
else
puts calc.quote(arg.to_f)
end
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