Connect With Us

Factory Design Pattern in Ruby

By Kapil Raj Nakhwa on Nov, 18, 2016 in factory pattern, design pattern, factory design pattern, programming, ruby, design pattern in ruby

Factory method lets class defer instantiation to subclasses. Factory method is primarily used to create an instance of a class in a more flexible way. Instead of creating instance of a class directly through new, it is created by another class called as factory.

Example:

require_relative 'shape_factory'

shape_factory = ShapeFactory.new
circle = shape_factory.get_shape('CIRCLE')
circle.draw

rectangle = shape_factory.get_shape('RECTANGLE')
rectangle.draw

square = shape_factory.get_shape('SQUARE')
square.draw

Factory Design Pattern

Implementation of Factory Design Pattern:

A factory class can be implemented in several ways, depending on the type of problem you are trying to solve:

  • The Creator class can be an abstract class.
  • The Creator class can be a concrete class with the Factory method as a virtual function.
  • The Factory method may be parameterized to allow creation of different types of objects.
  • The Factory method can be a static method.
  • The Factory method can be an instance method.

Let’s start with an example:

A class named ‘Duck’ which has methods eat, speak and sleep.

class Duck
  def initialize(name)
    @name = name
  end    

  def eat
    puts("Duck  #{@name} is eating.")
  end

  def speak
    puts("Duck  #{@name} says Quack!.")
  end

  def sleep
    puts("Duck  #{@name} sleeps quietly.")
  end
end

Now lets add a class Pond in which we will be adding number of ducks who speak, eat and sleep.

class Pond
  def initialize(number_ducks)
    @ducks = []
    number_ducks.times do |i|
      duck = Duck.new("Duck#{i}")
      @ducks << duck
    end

    def simulate_one_day
      @ducks.each { |duck| duck.speak }
      @ducks.each { |duck| duck.eat }
      @ducks.each { |duck| duck.sleep }
    end
  end

  pond = Pond.new(3)
  pond.simulate_one_day
end

Output:

    Duck Duck0 says Quack!
    Duck Duck1 says Quack!
    Duck Duck2 says Quack!
    Duck Duck0 is eating.
    Duck Duck1 is eating.
    Duck Duck2 is eating.
    Duck Duck0 sleeps quietly.
    Duck Duck1 sleeps quietly.
    Duck Duck2 sleeps quietly.

Since there are ducks in the pond, let’s add Frog as well.

So adding a class Frog who does the same i.e eat, speak and sleep.

class Frog
  def initialize(name)
    @name = name
  end

  def eat
    puts("Frog  #{@name} is eating.")
  end

  def speak
    puts("Frog  #{@name} says Croooaaak")
  end

  def sleep
    puts("Frog  #{@name} doesn’t sleep; he croaks all night!.")
  end
end

Now what happens if we initialize Pond? Only ducks will be added but we are adding Frogs as well.

Since we are adding frogs also we will need to change the Pond’s initialize method.

class Pond
  def initialize(number_animals)
    @animals = []
    number_animals.times do |i|
      animal = new_animal("Animal#{i}")
      @animals << animal
    end
  end

  def simulate_one_day
    @animals.each { |animal| animal.speak }
    @animals.each { |animal| animal.eat }
    @animals.each { |animal| animal.sleep }
  end
end

So adding new class which adds Duck to the Pond and Frogs to the Pond

class DuckPond < Pond
  def new_animal(name)
    Duck.new(name)
  end
end

class FrogPond < Pond
  def new_animal(name)
    Frog.new(name)
  end
end

pond = FrogPond.new(3)
pond.simulate_one_day

And we get all sorts of slimy green goings on:

    Frog Animal0 says Croooaaak!
    Frog Animal1 says Croooaaak!
    Frog Animal2 says Croooaaak!
    Frog Animal0 is eating.
    Frog Animal is eating.
    Frog Animal2 is eating.

Since a pond can have green plants, let’s add plants to the pond

class Algae
  def initialize(name)
    @name = name
  end

  def grow
    puts("The Algae #{@name} soaks up the sun and grows")
  end
end

class WaterLily
  def initialize(name)
    @name = name
  end

  def grow
    puts("The Waterlily  #{@name} floats, soaks up the sun and grows")
  end
end

Now modifying the class Pond to deal with plants as well

class Pond
  def initialize(number_animals, number_plants)
    @animals = []
    number_animals.times do |i|
      animal = new_animal("Animal#{i}")
      @animals << animal
    end

    @plants = []
    number_plants.times do |i|
      plant = new_plant("Plant#{i}")
      @plants << plant
    end
  end

  def simulate_one_day
    @plants.each { |plant| plant.grow }
    @animals.each { |animal| animal.speak }
    @animals.each { |animal| animal.eat }
    @animals.each { |animal| animal.sleep }
  end
end

class DuckWaterLilyPond < Pond
  def new_animal(name)
    Duck.new(name)
  end

  def new_plant(name)
    WaterLily.new(name)
  end
end

class Pond
  def initialize(number_animals, number_plants)
    @animals = []
    number_animals.times do |i|
      animal = new_organism(:animal, "Animal#{i}")
      @animals << animal
    end

    @plants = []
    number_plants.times do |i|
      plant = new_organism(:plant, "Plant#{i}")
      @plants << plant
    end
  end

  def simulate_one_day
    @plants.each { |plant| plant.grow }
    @animals.each { |animal| animal.speak }
    @animals.each { |animal| animal.eat }
    @animals.each { |animal| animal.sleep }
  end
end

class DuckWaterLilyPond < pond
  def new_organism(type, name)
    if type == :animal
      Duck.new(name)
    elsif type == :plant
      WaterLily.new(name)
    else
      raise "Unknown organism type: #{type}"
    end
  end
end

But those subclasses are needed to continue adding as per the nature of the pond.

class Pond
  def initialize(number_animals, animal_class, number_plants, plant_class)
    @animal_class = animal_class
    @plant_class = plant_class

    @animals = []
    number_animals.times do |i|
      animal = new_organism(:animal, "Animal#{i}")
      @animals << animal
    end

    @plants = []
    number_plants.times do |i|
      plant = new_organism(:plant, "Plant#{i}")
      @plants << plant
    end
  end

  def simulate_one_day
    @plants.each { |plant| plant.grow }
    @animals.each { |animal| animal.speak }
    @animals.each { |animal| animal.eat }
    @animals.each { |animal| animal.sleep }
  end

  def new_organism(type, name)
    if type == :animal
      @animal_class.new(name)
    elsif type == :plant
      @plant_class.new(name)
    else
      raise "Unknown organism type: #{type}"
    end
  end
end

Oh this is great! But since Pond cannot be alone, let’s add a Jungle around the Pond now

class Tree
  def initialize(name)
    @name = name
  end
  def grow
    puts("The tree #{@name} grows tall.")
  end
end

class Tiger
  def initialize(name)
    @name = name
  end
  def eat
    puts("Tiger #{@name} eats anything it wants.")
  end

  def speak
    puts("Tiger #{@name} Roars!.")
  end

  def sleep
    puts("Tiger #{@name} sleeps anywhere it wants.")
  end
end

Okay so the class name Pond is not any more appropriate ! let’s rename it to Habitat.

jungle = Habitat.new(1, Tiger, 4, Tree)
jungle.simulate_one_day

pond = Habitat.new( 2, Duck, WaterLily )
pond.simulate_one_day

Why is this design still not correct ?

unstable = Habitat.new(  2, Tiger, 4, WaterLily )

What if our design now needs more extensions to fungi, insects, etc.

Introducing abstract Factories

class JungleOrganismFactory
  def new_animal(name)
    Tiger.new(name)
  end

  def new_plant(name)
    Tree.new(name)
  end
end

When to use

  • When the client doesn't know which class it may require at runtime.
  • A class wants its subclasses to specify the objects it creates.
  • You want to encapsulate creation of objects.
  • Object instance needs to be initialized with some data not available to the client.
  • Object instantiation requires lot of data and there are lots of variations based on the data. Instead provide static Factory Methods that create the instances based on different variations.

Related Patterns

  • Factory Method has a lot of variations. It can return a new object or the same instance multiple times, or can return a subclass object by extending a new class.
  • Factory Method is usually called through a Template Method and is usually a hook that subclasses can override for custom implementation. E.g. override the Factory Method and provide a different implementation.
  • Used to implement Abstract Factory.
  • It is a flexible replacement for new as it encapsulates creation.
  • Factory Method has to be subclassed if it has to return a new object. Prototypes don’t require a new class; it requires a new object.
  • Prototypes require initialize operations after returning an instance; Factory Methods don’t require such operation.

Slideshare: http://www.slideshare.net/Jyaasa/factory-design-pattern-67324901?platform=hootsuite

Reference:

Share This:

Comments

sagun shrestha
Nice! :)
 November 15, 2016
bimal prasad pandey
Factory Design Pattern seems similar to Law Of Demeter, is it true ?? confused
 November 15, 2016
Kapil
No, Bimal .. They are completely different and dictate complete different things. While Factory Pattern is a solution to a problem where you have to adapt to various conditions during the instantiation of an object. Law of demeter governs the messaging between objects
 November 16, 2016
latem999
That's literally the code from Russ Olsen's book. You didn't even bother to change the strings in the sleep, eat etc. methods.
 December 28, 2016

Add a new comment

Latest Posts


First Step to Software Development: Design Thinking Process By Rasna Shakya on Nov, 03, 2017

Bourbon By Rasna Shakya on Oct, 20, 2017

Design Sprint By Neha Suwal on Aug, 14, 2017

3 years of helping startups and entrepreneurs with technology By Neha Suwal on Aug, 04, 2017

Digital Marketing for Startups By Neha Suwal on Jun, 30, 2017