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...