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

RoleIn this exampleResponsibility
CreatorWaveDeclares the factory method and uses its result in fixed code (#enemies,
Concrete CreatorGoblinWave, TrollWaveOverrides the factory method to make a particular enemy.
Product#name, #hpThe duck type every enemy shares.
Concrete ProductGoblin, TrollThe objects actually made.

Ruby implementation

factory_method.rb
# 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
end

In 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.
  • NotImplementedError in 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.