Factory Pattern
Part of the Object Oriented Programming section of Coddy's Ruby journey. Lesson 43 of 57.
The factory pattern puts object creation in one place. Callers ask for what they need, often by name, and receive a ready object without knowing which class was built:
class Square
def initialize(side)
@side = side
end
def area
@side * @side
end
end
class Circle
def initialize(r)
@r = r
end
def area
(3.14159 * @r * @r).round(2)
end
end
class ShapeFactory
def self.create(kind, size)
case kind
when "square" then Square.new(size)
when "circle" then Circle.new(size)
end
end
end
puts ShapeFactory.create("square", 3).area
puts ShapeFactory.create("hexagon", 3).inspectOutput:
9
nilA hash from names to classes makes the factory shorter and easier to extend. Classes are objects, so they can be stored in a hash and sent new:
class Dog
def speak
"Woof"
end
end
class Cat
def speak
"Meow"
end
end
class AnimalFactory
KINDS = { "dog" => Dog, "cat" => Cat }.freeze
def self.create(kind)
klass = KINDS.fetch(kind) { raise ArgumentError, "unknown animal: #{kind}" }
klass.new
end
end
puts AnimalFactory.create("cat").speak
begin
AnimalFactory.create("cow")
rescue ArgumentError => e
puts e.message
endOutput:
Meow
unknown animal: cowA factory can also let other code register new kinds at run time, so adding a kind does not mean editing the factory:
class Plugins
@kinds = {}
class << self
def register(name, klass)
@kinds[name] = klass
end
def build(name)
@kinds.fetch(name).new
end
def names
@kinds.keys.sort
end
end
end
class Zip
def run
"zipping"
end
end
Plugins.register("zip", Zip)
puts Plugins.build("zip").run
p Plugins.namesOutput:
zipping
["zip"]The callers depend only on what the objects can do, here area or speak, never on the concrete classes. A new shape means one new class and one new entry in the factory, and no change anywhere else:
shape = ShapeFactory.create(kind, size) # Square, Circle, ...
puts shape.area # the caller never names a classChallenge
EasyComplete the shapes and ShapeFactory.create(kind, size). Square, Circle and Triangle (equilateral) share the base class Shape, whose to_s returns square 3: area 9.00, with the class name in lower case, the size and the area with two decimals. Use 3.14159 for pi and Math.sqrt(3) / 4 * size**2 for the triangle. The factory looks the kind up in a frozen hash constant KINDS and returns nil for an unknown kind.
The supplied code reads lines square 3, asks the factory for each shape and prints it, or unknown shape: hexagon.
Your code goes in shape.rb, square.rb, circle.rb, triangle.rb and shape_factory.rb. main.rb holds the supplied input/output code and cannot be edited.
Try it yourself
require_relative 'shape'
require_relative 'square'
require_relative 'circle'
require_relative 'triangle'
require_relative 'shape_factory'
# Supplied input/output code: keep it as it is
input = []
while (line = gets)
input << line.chomp
end
input.each do |line|
kind, size = line.split
shape = ShapeFactory.create(kind, size.to_f)
puts shape || "unknown shape: #{kind}"
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