Шаблон строителя
Часть раздела Объектно-ориентированное программирование путешествия по Ruby на Coddy. Урок 48 из 57.
Паттерн строитель создаёт сложный объект шаг за шагом. Каждый шаг представляет собой метод, который сохраняет один вариант и возвращает строитель, поэтому шаги образуют цепочку; build проверяет выбранные варианты и возвращает готовый объект:
class Pizza
def initialize(size, toppings)
@size = size
@toppings = toppings
end
def to_s
"#{@size} pizza with #{@toppings.join(", ")}"
end
end
class PizzaBuilder
def initialize
@toppings = []
end
def size(value)
@size = value
self
end
def topping(name)
@toppings << name
self
end
def build
raise ArgumentError, "size is required" if @size.nil?
Pizza.new(@size, @toppings)
end
end
puts PizzaBuilder.new.size("large").topping("cheese").topping("ham").buildРезультат:
large pizza with cheese, hamПоскольку строитель создаёт объект только в build, он может сразу проверить всю комбинацию, например обязательную часть или два параметра, которые нельзя использовать вместе:
class TicketBuilder
def seat(value)
@seat = value
self
end
def standing
@standing = true
self
end
def build
raise ArgumentError, "seat and standing exclude each other" if @seat && @standing
@standing ? "standing ticket" : "ticket for seat #{@seat || "any"}"
end
end
puts TicketBuilder.new.seat("B7").build
puts TicketBuilder.new.standing.build
begin
TicketBuilder.new.seat("A1").standing.build
rescue ArgumentError => e
puts e.message
endВывод:
ticket for seat B7
standing ticket
seat and standing exclude each otherРаспространённая форма Ruby передаёт построитель блоку. Метод класса создаёт построитель, передаёт его в блок и в конце вызывает build:
class Email
attr_accessor :to, :subject, :lines
def self.compose
email = new
email.lines = []
yield email
email
end
def to_s
"To: #{to}\nSubject: #{subject}\n\n#{lines.join("\n")}"
end
end
mail = Email.compose do |e|
e.to = "ada@x.io"
e.subject = "Hi"
e.lines << "Hello Ada,"
e.lines << "see you soon."
end
puts mailВывод:
To: ada@x.io
Subject: Hi
Hello Ada,
see you soon.Для небольшого числа необязательных значений часто достаточно ключевых аргументов Ruby со значениями по умолчанию. Билдер оправдан, когда части могут повторяться, важен порядок или комбинацию нужно проверить до создания объекта:
Pizza.new(size: "large", crust: "thin") # несколько опций: ключевые слова
Query.new.from("users").where("age > 18").where("active = 1").limit(10).to_sЗадание
ЛегкоЗавершите PizzaBuilder и Pizza. Шаги построителя size(value), crust(value) и topping(name) (который можно повторять) возвращают построитель. build вызывает исключение ArgumentError с сообщением size is required, если размер не указан, использует корж classic, если он не выбран, и возвращает объект Pizza. Pizza#to_s возвращает large thin pizza with cheese, ham или small classic pizza with no toppings.
Каждая входная строка представляет собой один заказ, например size=large crust=thin topping=cheese topping=ham. Предоставленный код по очереди передаёт каждую часть построителю, связывая вызовы в цепочку, и выводит пиццу или сообщение об ошибке.
Ваш код должен находиться в pizza.rb и pizza_builder.rb. В main.rb содержится предоставленный код ввода/вывода, и его нельзя изменять.
Попробуйте сами
require_relative 'pizza'
require_relative 'pizza_builder'
# Предоставленный код ввода/вывода: оставьте его как есть
input = []
while (line = gets)
input << line.chomp
end
input.each do |line|
builder = line.split.reduce(PizzaBuilder.new) do |b, part|
step, value = part.split("=")
b.public_send(step, value)
end
begin
puts builder.build
rescue ArgumentError => e
puts e.message
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