Factory Method
Define an interface for creating an object, but let subclasses decide which class to instantiate. Factory Method lets a class defer instantiation to subclasses.
Also known as: Virtual Constructor
Participants
| Role | In this example | Responsibility |
|---|---|---|
| Creator | Wave | Declares the factory method and uses its result in fixed code (#enemies, |
| Concrete Creator | GoblinWave, TrollWave | Overrides the factory method to make a particular enemy. |
| Product | #name, #hp | The duck type every enemy shares. |
| Concrete Product | Goblin, Troll | The objects actually made. |
Ruby implementation
# Products: the enemies a wave can be made of.
class Goblin
def name = "Goblin"
def hp = 30
end
class Troll
def name = "Troll"
def hp = 320
end
# The Creator. #enemies is fixed; which enemy it makes is not.
class Wave
def initialize(size)
@size = size
end
def enemies
Array.new(@size) { enemy }
end
def total_hp
enemies.sum(&:hp)
end
private
# The factory method. Subclasses decide which enemy to make.
def enemy
raise NotImplementedError, "#{self.class} must define #enemy"
end
end
# Concrete Creators: each one overrides only the factory method.
class GoblinWave < Wave
private
def enemy = Goblin.new
end
class TrollWave < Wave
private
def enemy = Troll.new
endIn use
GoblinWave.new(3).enemies.map(&:name)
# => ["Goblin", "Goblin", "Goblin"]
TrollWave.new(2).total_hp
# => 640
Wave decides how many enemies there are and what to do with them. Each subclass decides only which enemy to make.
Notes
Ruby idioms
- The factory method is private and takes no arguments. It’s an internal decision of the class, not part of its public interface.
NotImplementedErrorin the base class marks the method subclasses must provide, and fails loudly if one forgets.- Often you don’t need a subclass at all. Because classes are objects, you can pass the product class in:
Wave.new(3, enemy: Goblin). Use Factory Method when the choice really belongs to the subclass.
Reach for it when
- A base class owns some logic, but one object it creates has to vary.
- A framework needs application code to supply objects, such as test-case classes or ActiveRecord’s
instantiate.
Watch for
- A parallel class hierarchy, with one creator per product, can grow quickly. With many products, an Abstract Factory or plain injection may be cleaner.
Factory Method is often one step inside a Template Method.