Patrón Builder
Parte de la sección Programación orientada a objetos del Journey de Ruby de Coddy. Lección 48 de 57.
El patrón builder crea un objeto complejo paso a paso. Cada paso es un método que almacena una opción y devuelve el builder, por lo que los pasos se encadenan; build comprueba las opciones y devuelve el objeto terminado:
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").buildSalida:
large pizza with cheese, hamComo el constructor solo crea el objeto en build, puede comprobar toda la combinación de una vez, como una parte obligatoria o dos opciones que no pueden combinarse:
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
endSalida:
ticket for seat B7
standing ticket
seat and standing exclude each otherUna forma común de Ruby pasa el creador a un bloque. El método de clase crea el creador, se lo cede y llama a build al 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 mailSalida:
To: ada@x.io
Subject: Hi
Hello Ada,
see you soon.Para unos pocos valores opcionales, los argumentos con nombre de Ruby y valores predeterminados suelen ser suficientes. Un builder resulta útil cuando las partes pueden repetirse, el orden importa o es necesario comprobar la combinación antes de que exista el objeto:
Pizza.new(size: "large", crust: "thin") # pocas opciones: keywords
Query.new.from("users").where("age > 18").where("active = 1").limit(10).to_sDesafío
FácilCompleta PizzaBuilder y Pizza. Los pasos del constructor size(value), crust(value) y topping(name) (repetible) devuelven el constructor. build genera ArgumentError con size is required cuando no se ha proporcionado ningún tamaño, usa la corteza classic cuando no se ha elegido ninguna y devuelve una Pizza. Pizza#to_s devuelve large thin pizza with cheese, ham o small classic pizza with no toppings.
Cada línea de entrada es un pedido, como size=large crust=thin topping=cheese topping=ham. El código proporcionado envía cada parte al constructor en orden, encadenándolas, e imprime la pizza o el mensaje de error.
Tu código debe ir en pizza.rb y pizza_builder.rb. main.rb contiene el código proporcionado de entrada/salida y no se puede editar.
Pruébalo tú mismo
require_relative 'pizza'
require_relative 'pizza_builder'
# Código de entrada/salida suministrado: déjalo tal 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 lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.
Todas las lecciones de Programación orientada a objetos
1Clases y objetos
Trabajar con archivosDefinir clasesVariables de instanciaMétodos y selfto_s e inspectRepaso: tarjeta de biblioteca4Encapsulación
Métodos privadosMétodos protegidosAsignadores privadosProtección del estadoRepaso: máquina expendedora7Polimorfismo y tipado pato
PolimorfismoTipado patoMétodos abstractoscase y clasesRepaso: métodos de pago2Accesores y self
Lectores y escritoresSetters personalizadosself en los métodosIdentidad e igualdadRepaso: lista de contactos5Herencia
SubclasesSobrescritura de métodosLlamar a superis_a? y ancestrosRepaso - Nómina del personal8Métodos especiales
Igualdad y hashMétodos de operadoresIndexación y adiciónColecciones personalizadasRepaso: fracciones11Proyecto: Gestión de biblioteca
Libros y miembrosPréstamo de librosPractica por tu cuenta: Compilador de Ruby online