-
Notifications
You must be signed in to change notification settings - Fork 13
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
xponrails
committed
Jun 19, 2010
1 parent
f6cf588
commit ec33d3c
Showing
7 changed files
with
403 additions
and
0 deletions.
There are no files selected for viewing
144 changes: 144 additions & 0 deletions
144
creational_patterns/abstract_factory/abstract_factory_en.textile
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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
144
creational_patterns/abstract_factory/abstract_factory_it.textile
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
Oops, something went wrong.