Skip to content

Commit

Permalink
first commit for abstract factory
Browse files Browse the repository at this point in the history
  • Loading branch information
xponrails committed Jun 19, 2010
1 parent f6cf588 commit ec33d3c
Show file tree
Hide file tree
Showing 7 changed files with 403 additions and 0 deletions.
144 changes: 144 additions & 0 deletions creational_patterns/abstract_factory/abstract_factory_en.textile
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
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:
<code lang="ruby">
#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
</code>
How we can see, both models derive from a common superclass Game.

Let's define the Factories delegate to build these objects:
<code lang="ruby">
#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
</code>
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:
<code lang="ruby">
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
</code>

At this point, launching the following file main.rb

<code lang="ruby">
#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
</code>

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:
<code lang="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
</code>
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.
144 changes: 144 additions & 0 deletions creational_patterns/abstract_factory/abstract_factory_it.textile
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
Una Factory astratta fornisce un'interfaccia comune per creare famiglie di oggetti tra loro relazionate.
L'oggetto client non si preoccuperà di costruire direttamente gli oggetti di cui avrà bisogno, ma chiamerà i metodi forniti da questa interfaccia comune.

Vediamo di seguito una possibile implementazione di una Factory astratta e delle relative Factories concrete che la implementano.

Supponiamo di avere due categorie di giochi come classi model:
<code lang="ruby">
#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
</code>
Come si puo' notare, entrambi derivano da una superclasse comune Game.

Definiamo ora le Factories incaricate a costruire questi oggetti:
<code lang="ruby">
#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
</code>
Notiamo che è stata definita una Factory astratta (MyAbstractGameFactory) come modulo: questa definisce il metodo astratto create che deve essere implementato dalla classe che la include.
RpgFactory e ArcadeFactory rappresentano le due Factory concrete incaricate di costruire rispettivamente giochi di tipo Rpg ed Arcade.

Il codice di un eventuale GameStore, in grado di fornire giochi secondo le esigenze del cliente, sarà così definito:
<code lang="ruby">
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
</code>

A questo punto, lanciando il seguente file main.rb

<code lang="ruby">
#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
</code>

otterremo in console il seguente output:

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

Ottimizziamo a questo punto le nostre Factories, in modo da sfruttare le potenzialità offerte da Ruby:
<code lang="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
</code>
Come si puo' notare, ora un'unica Factory concreta GameFactory si preoccuperà di costruire Rpg e Arcade in base all'esigenza del momento.
In questo modo si permette che un sistema sia indipendente dall'implementazione degli oggetti concreti e che il client, attraverso l'interfaccia, utilizzi le diverse famiglie di prodotti.
Da notare che in entrambi gli esempi la definizione del modulo MyAbstractGameFactory ha di fatto solo uno scopo didattico e serve per "simulare" in Ruby un eventuale metodo astratto di una classe astratta.

L'output generato invocando questo nuovo GameStore è lo stesso visto nel precedente esempio.
42 changes: 42 additions & 0 deletions creational_patterns/abstract_factory/factories.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
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

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
40 changes: 40 additions & 0 deletions creational_patterns/abstract_factory/factories2.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
module MyAbstractGameFactory
def create(title)
raise NotImplementedError, "You should implement this method"
end
end

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+1}")
end
end

def show_games
@games.each {|game| game.description}
end
end
8 changes: 8 additions & 0 deletions creational_patterns/abstract_factory/main.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
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

7 changes: 7 additions & 0 deletions creational_patterns/abstract_factory/main2.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
require 'models.rb'
require 'factories2.rb'

game_store = GameStore.new(2, :rpg)
game_store.show_games
game_store = GameStore.new(5, :arcade)
game_store.show_games
Loading

0 comments on commit ec33d3c

Please sign in to comment.