Design Patterns in Ruby: Abstract Factory

An abstract Factory provides a common interface for creating families of related objects together. The client object does not bother to build objects directly, but it calls the methods provided by this common interface. Below is showed one possible implementation of an abstract Factory and its concrete Factories that implement it. Suppose we have two categories of games as a model classes:


#models.rb
class Game
  attr_accessor :title
  def initialize(title)
    @title = title
  end
end

class Rpg < Game
  def description
    puts "I am a RPG named #{@title}"
  end
end

class Arcade < Game
  def description
    puts "I am an Arcade named #{@title}"
  end
end 

How we can see, both models derive from a common superclass Game. Let's define the Factories delegate to build these objects:

#factories.rb
module MyAbstractGameFactory
  def create(title)
    raise NotImplementedError, "You should implement this method"
  end
end

class RpgFactory
  include MyAbstractGameFactory
  def create(title)
    Rpg.new title
  end
end

class ArcadeFactory
  include MyAbstractGameFactory
  def create(title)
    Arcade.new title
  end
end  

Note that we have defined the abstract factory (MyAbstractGameFactory) as a module: it defines the abstract method that must be implemented by the class that includes it. RpgFactory and ArcadeFactory represent the two concrete factories responsible to build, respectively, Arcade and RPG games. The code of a GameStore, that can provide games basing on the needs of the customer, will be defined as follows:

class GameStore
  def initialize(number_of_games, game_type)  
    if game_type == :rpg
      title = 'Final Fantasy'
      game_factory = RpgFactory.new
    elsif game_type== :arcade
      title = 'Double Dragon'
      game_factory = ArcadeFactory.new
    end
        
    @games = []
    number_of_games.times do |i|
      @games << game_factory.create("#{title} #{i+1}")
    end
  end 
  
  def show_games
    @games.each {|game| game.description} 
  end
end

At this point, launching the following file main.rb

#main.rb
require 'models.rb'
require 'factories.rb'
game_store = GameStore.new(2, :rpg)
game_store.show_games
game_store = GameStore.new(5, :arcade)
game_store.show_games  

we'll get the following output in console: I am a RPG named Final Fantasy 1 I am a RPG named Final Fantasy 2 I am an Arcade named Double Dragon 1 I am an Arcade named Double Dragon 2 I am an Arcade named Double Dragon 3 I am an Arcade named Double Dragon 4 I am an Arcade named Double Dragon 5 At this point we can optimize our Factories in order to take advantage of the potential offered by Ruby:

#factories2.rb
class GameFactory
  include MyAbstractGameFactory
  
  def initialize(game_class)
    @game_class = game_class
  end
  
  def create(title)
    @game_class.new title
  end
end 

class GameStore
  def initialize(number_of_games, game_type)
    c = Object.const_get(game_type.to_s.capitalize)

    game_factory = GameFactory.new(c)
    
    if game_type == :rpg
      title = 'Final Fantasy'
    elsif game_type == :arcade
      title = 'Double Dragon'
    end
    
    @games = []
    number_of_games.times do |i|
      @games << game_factory.create("#{title} #{i}")
    end
  end 
  
  def show_games
    @games.each {|game| game.description} 
  end
end

As we can see, now a single concrete factory GameFactory continues to build Arcade and RPG basing on the need of the moment. This will allow a system to be independent from the implementation of concrete objects and that the client, through the interface, to use the different product families. Note that in both examples the definition of MyAbstractGameFactory was made only for educational purposes and was used to "simulate" in Ruby an abstract method of an abstract class. The output generated by invoking this new GameStore is the same as seen in the previous example.


All code available in this repository on GitHub: "design_patterns_in_ruby": http://github.com/devinterface/design_patterns_in_...

Source code "here": http://github.com/devinterface/design_patterns_in_...

Previous posts from this serie:
"Design Patterns in Ruby: Introduction": http://www.devinterface.com/blog/2010/06/design-patter...