Factory pattern

From wikinotes

A class/method responsible for choosing/instantiating a class for you.


Basics

Given classes Cat, Dog that implement interface Animal,
We want a factory that chooses the desired pet.

module Animal
  interface!

  sig { abstract.returns(String) }
  def speak; end
end

class Cat
  include Animal

  sig { override.returns(String) }
  def speak
    "meow"
  end
end

class Dog
  include Animal

  sig { override.returns(String) }
  def speak
    "woof"
  end
end
class PetFactory
  def from(interests)
    return Cat.new if interests.include?(:homebody)

    Dog.new
  end
end

Registering Concretions

Subclass Hooks

NOTE:

careful with evaluation order. If these hooks are evaluated lazily, your register will be empty (ex. ruby/python)

If your language supports subclass hooks, even without eager loading you can register concretions automatically.
reusing the previous example:


class AnimalBase
  # when inheritance is read, is registered with the superclass's '@register'
  def self.inherited(concretion)
    register(concretion)
  end

  def self.register(concretion)
    @register << concretion
  end

  def self.from(interests)
    @register.find { |concretion| concretion.handles?(interests) }
  end
end
class Cat < AnimalBase
  include Animal

  def speak
    "meow"
  end

  def self.handles?(interests)
    interests.include?(:homebody)
  end
end

class Dog < AnimalBase
  include Animal

  def speak
    "woof"
  end

  def self.handles?(interests)
    !interests.include?(:homebody)
  end
end
class AnimalBase
  def self.from(interests)
    AnimalBase.from(interests)
  end
end

Ordered Array

Otherwise you can keep an array classes, and pass through it to determine the class to choose

class PetFactory
  def self.from(interests)
    [Cat, Dog].find { |animal| animal.handles?(interests) }.new
  end
end