Padrão Builder
Parte da seção Programação orientada a objetos do Journey de Ruby da Coddy. Lição 48 de 57.
O padrão builder cria um objeto complexo passo a passo. Cada etapa é um método que armazena uma escolha e retorna o builder, encadeando as etapas; build verifica as escolhas e retorna o objeto concluído:
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").buildSaída:
large pizza with cheese, hamComo o builder só cria o objeto em build, ele pode verificar toda a combinação de uma só vez, como uma parte obrigatória ou duas opções que não podem ser usadas juntas:
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
endSaída:
ticket for seat B7
standing ticket
seat and standing exclude each otherUma forma comum em Ruby passa o builder para um bloco. O método da classe cria o builder, faz yield dele e chama build no final:
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 mailSaída:
To: ada@x.io
Subject: Hi
Hello Ada,
see you soon.Para um pequeno conjunto de valores opcionais, os argumentos nomeados do Ruby com valores padrão geralmente são suficientes. Um builder vale a pena quando as partes podem se repetir, a ordem é importante ou a combinação precisa ser verificada antes de o objeto existir:
Pizza.new(size: "large", crust: "thin") # poucas opções: keywords
Query.new.from("users").where("age > 18").where("active = 1").limit(10).to_sDesafio
FácilConclua PizzaBuilder e Pizza. As etapas do construtor size(value), crust(value) e topping(name) (repetível) retornam o construtor. build gera ArgumentError com size is required quando nenhum tamanho foi informado, usa a massa classic quando nenhuma foi escolhida e retorna uma Pizza. Pizza#to_s retorna large thin pizza with cheese, ham ou small classic pizza with no toppings.
Cada linha de entrada é um pedido, como size=large crust=thin topping=cheese topping=ham. O código fornecido envia cada parte ao construtor na ordem, encadeando as chamadas, e imprime a pizza ou a mensagem de erro.
Seu código deve ficar em pizza.rb e pizza_builder.rb. main.rb contém o código fornecido de entrada/saída e não pode ser editado.
Experimente você mesmo
require_relative 'pizza'
require_relative 'pizza_builder'
# Código de entrada/saída fornecido: mantenha-o como está
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
Esta lição inclui um quiz rápido. Comece a lição para respondê-lo e acompanhar seu progresso.
Todas as lições de Programação orientada a objetos
1Classes e Objetos
Trabalhando com ArquivosDefinindo ClassesVariáveis de InstânciaMétodos e selfto_s e inspectRevisão - Cartão da Biblioteca4Encapsulamento
Métodos privadosMétodos protegidosSetters privadosProtegendo o estadoRevisão - Máquina de venda automática7Polimorfismo e Duck Typing
PolimorfismoDuck TypingMétodos Abstratoscase e ClassesRevisão - Métodos de Pagamento2Acessores e self
Leitores e GravadoresSetters Personalizadosself nos MétodosIdentidade e IgualdadeRecapitulação - Lista de Contatos5Herança
SubclassesSobrescrita de métodosChamando superis_a? e ancestraisRevisão - Folha de pagamento da equipe8Métodos Especiais
Igualdade e hashMétodos de OperadoresIndexação e AdiçãoColeções PersonalizadasRevisão - Frações11Projeto: Gerenciamento de Biblioteca
Livros e MembrosEmpréstimo de LivrosPratique por conta própria: Compilador de Ruby online