Builder Pattern
Part of the Object Oriented Programming section of Coddy's Ruby journey. Lesson 48 of 57.
The builder pattern creates a complex object step by step. Each step is a method that stores one choice and returns the builder, so the steps chain; build checks the choices and returns the finished object:
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").buildOutput:
large pizza with cheese, hamBecause the builder only creates the object in build, it can check the whole combination at once, such as a required part or two options that cannot go together:
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
endOutput:
ticket for seat B7
standing ticket
seat and standing exclude each otherA common Ruby form hands the builder to a block. The class method creates the builder, yields it and calls build at the end:
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 mailOutput:
To: ada@x.io
Subject: Hi
Hello Ada,
see you soon.For a handful of optional values, Ruby's keyword arguments with defaults are often enough. A builder pays off when parts can repeat, order matters, or the combination needs checking before the object exists:
Pizza.new(size: "large", crust: "thin") # few options: keywords
Query.new.from("users").where("age > 18").where("active = 1").limit(10).to_sChallenge
EasyComplete PizzaBuilder and Pizza. The builder's steps size(value), crust(value) and topping(name) (repeatable) return the builder. build raises ArgumentError with size is required when no size was given, uses the crust classic when none was chosen, and returns a Pizza. Pizza#to_s returns large thin pizza with cheese, ham, or small classic pizza with no toppings.
Each input line is one order such as size=large crust=thin topping=cheese topping=ham. The supplied code sends each part to the builder in order, by chaining, and prints the pizza or the error message.
Your code goes in pizza.rb and pizza_builder.rb. main.rb holds the supplied input/output code and cannot be edited.
Try it yourself
require_relative 'pizza'
require_relative 'pizza_builder'
# Supplied input/output code: keep it as it is
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
This lesson includes a short quiz. Start the lesson to answer it and track your progress.
All lessons in Object Oriented Programming
1Classes and Objects
Working With FilesDefining ClassesInstance VariablesMethods and selfto_s and inspectRecap - Library Card7Polymorphism and Duck Typing
PolymorphismDuck TypingAbstract Methodscase and ClassesRecap - Payment Methods2Accessors and Self
Readers and WritersCustom Settersself in MethodsIdentity and EqualityRecap - Contact List8Special Methods
Equality and hashOperator MethodsIndexing and AppendingCustom CollectionsRecap - Fractions11Project: Library Management
Books and MembersBorrowing Books3Class Methods and Variables
Class MethodsClass Instance VariablesConstantsFactory MethodsRecap - Coupon CodesPractice on your own: Online Ruby compiler