factory method pattern in ruby
whatโs in a pattern?
Conventional programatic design patterns come in 3 flavors ๐ฆ:
- creational
- these guys create objects dynamically and re-use code
- structural
- explains how to assemble objects while keeping flexibility
- behavioural
- converned with algorithms and objects responsibilities
But today we are going to pick on the factory pattern, which our dear friends at wikipedia describe as below.
the factory method pattern is a creational pattern that uses factory methods to deal with the problem of creating objects without having to specify the exact class of the object that will be created.
but for a factory the kingdom was lost
Now to dive into what a factory pattern can do for your codebase, the <label>
describes it as being able to โcreateโ Classes or Objects in a scalable & dynamic way. All the while allowing your code to be flexible, malleable, and ductile to futures changes or added features. So at a high level fly by, code written with a factory pattern in mind will be able to instantiate objects of the Classes of the problem domain it traverses.

figure_1
so below will work just fine for figure_1
but will get pretty cray cray real quick if you want to start and add extra freight types. You could try and hide your sins inside a case
statement, but theyโll still be there ๐ง.
order_data = { line001: '2 cats', line002: '3 bunnies', line003: '3 ferrets' }
class OrderGenerator
def self.generate(order, type)
if type == 'road'
order.each_pair { |key, value| puts "#{key} of #{value}" }
puts 'Delivered by ๐'
elsif type == 'sea'
order.each_pair { |key, value| puts "#{key} of #{value}" }
puts 'Delivered by ๐ฅ๏ธ'
else
raise 'Unsupported type of transport'
end
end
end
OrderGenerator.generate(order_data, 'sea')
#...
line001 of 2 cats
line002 of 3 bunnies
line003 of 3 ferrets
Delivered by ๐ฅ๏ธ
figure_2
Here we are using the factory pattern buy instantiating the Transport Factory Class form within the existing OrderGenerator
class. Now we can see that we are able to greatly extend the types of transport available to fullfil the client orders. We are also not hiding our transport types within a massive if/else
or case
statement.
order_data = { line001: '2 cats', line002: '3 bunnies', line003: '3 ferrets' }
class RoadLogistics
def deliver(order)
return '' if order.empty?
order.each_pair { |key, value| puts "#{key} of #{value}" }
puts 'Delivered by ๐'
end
end
class SeaLogistics
def deliver(order)
return '' if order.empty?
order.each_pair { |key, value| puts "#{key} of #{value}" }
puts 'Delivered by ๐ฅ๏ธ'
end
end
class Transport
def self.for(type)
case type
when 'sea'
SeaLogistics.new
when 'road'
RoadLogistics.new
else
raise 'Unsupported type of transport'
end
end
end
class OrderGenerator
def self.generate(order, type)
Transport.for(type).deliver(order)
end
end
OrderGenerator.generate(order_data, 'road')
#...
line001 of 2 cats
line002 of 3 bunnies
line003 of 3 ferrets
Delivered by ๐
We can also see that figure_2
code reflects and Open/Closed principle of the SOLID object-oriented design. So the Factory design pattern ticks a lot of boxes for just a minimal amount if initial front loaded effort.
references: