Está en la página 1de 7

Franco Guidi Polanco 1

Abstract Factory (Fábrica abstracta -GoF)

Descripción

Presenta una interfaz para la creación de familias de productos, de forma tal que el
cliente que las utiliza no requiera conocer las clases concretas que la componen. Esto
fuerza al cliente a crear solamente productos “relacionados” entre si, a la vez que lo
habilita para la utilización de distintas familias de productos.

Ejemplo

Un negocio de venta de equipos musicales efectúa demostraciones del uso de sus


distintos productos. Los productos se agrupan en familias según la tecnología en la
que se basan, como por ejemplo la familia de equipos que utilizan compact disc (CD),
o la familia de equipos basados en cinta magnética. Independientemente del caso, se
supondrá que cada familia está compuesta por un medio en que se registra la música,
un dispositivo que realiza el registro en el respectivo medio, y otro que la reproduce
(ver Fig x.1).

Grabador CD Reproductor

Grabador Cinta magnética Reproductor

Fig. x.1

Si estos productos ofrecieran a sus usuarios la misma interfaz, un cliente podría reali-
zar el mismo proceso de prueba de productos en cualquiera de las familias. Por ejem-
plo, podría registrar una canción utilizando el dispositivo de grabación, para luego es-
cucharlo en el dispositivo de reproducción. Sólo deberíamos asegurarnos que los pro-
ductos sean compatibles entre si, esto es, que todos ellos pertenezcan a una misma
familia.

El problema consiste en la definición de un mecanismo que permita al cliente crear y


utilizar familias completas de productos, sin tener conocimiento de cuáles son los inte-
grantes concretos de la familia.

Descripción de la solución ofrecida por el patrón de diseño

La definición de interfaces constituye el mecanismo mediante el cual es posible ocultar


la implementación de una clase en aquellas porciones de código en que son utilizadas.
En consecuencia, la definición de interfaces comunes para aquellos productos análo-
gos pertenecientes a diferentes familias, permitirá a un mismo cliente realizar opera-
ciones similares sobre artefactos de tecnologías distintas. El problema surge cuando el
cliente debe manejar familias cuyos productos no deben entremezclarse. Por ejemplo,
Franco Guidi Polanco 2

si se pretende utilizar un grabador de CD el cliente debe instanciarlo junto con un CD y


un reproductor de CD, no con una cinta magnética o un reproductor de DVD. La sola
definición de interfaces para los productos no garantiza que el cliente se limite a crear
sólo productos de una misma familia.

El patrón de diseño Abstract Factory resuelve este problema por medio de la encapsu-
lación de reglas de instanciación. Ésta permite ocultar en una clase “fábrica” el proce-
so de instanciación de un conjunto de productos. Por su parte, la encapsulación de los
productos tras interfaces comunes permitirá al cliente utilizar productos análogos per-
tenecientes a familias diferentes.

Los neófitos en el campo de los patrones de diseño suelen cuestionarse la diferencia


entre este patrón y el Factory Method, puesto que ambos presentan una estructura
similar, al encapsular en una clase el proceso de instanciación de un objeto. La dife-
rencia radica en que, mientras el Factory Method tiene por objetivo diferir hacia una
determinada clase (o subclase) el tipo de producto a instanciar, el patrón Abstract Fac-
tory persigue garantizar la creación de un conjunto de productos relacionados.

La implementación de este patrón requiere definición de una interfaz adicional a la de


los productos: la interfaz que implementarán las “fábricas” encargadas de generarlos, y
que permitirá al cliente interactuar con ellas. Esta interfaz proveerá los métodos cuyo
contrato consistirá en retornar instancias de cada tipo de producto dentro de una fami-
lia. En el caso del ejemplo, la interfaz de las fábricas permitirá al cliente solicitar la ge-
neración de instancias de grabador, de medios de registro y de reproductores. De esta
forma, el cliente no deberá invocar en caso alguno el constructor de los productos,
quedando absolutamente desligado de la implementación particular de estos últimos.

Estructura del patrón de diseño

<<stereotipe>>
Client

<<stereotipe>> <<stereotipe>>
AbstractFactory AbstractProduct
{abstract} {abstract}

+createProduct() : AbstractProduct {abstract}

<<stereotipe>>
<<stereotipe>> <<instantiates>> ConcreteProduct
ConcreteFactory produces

#ConcreteProduct()
+createProduct() : AbstractProduct

Fig. x.2
Franco Guidi Polanco 3

Aplicación de patrón de diseño

Esquema

<<Interface>>
DevicesFactory
Client
+ createPlayer() : Player
+ createRecorder() : Recorder
+ createMedia() : Media

<<Interface>>
Media

CD Devices Tape Devices


Fact ory Factory

Tape CD

+ saveOnTape(String sound) + writeOnDisk(String sound)


+ readTape() : String + readDisk() : String

<<Interface>>
Recorder

+ accept(Media)
+ record()

Tape Recorder CD Recorder

<<Interface>>
Player

+ ac cept(Media)
+ play()

Tape Player CD Player

Fig. x.3

Participantes
ƒ AbstractFactory (Fábrica Abstracta): interfaz DevicesFactory.
- Declara una interfaz para las operaciones que crean y restituyen productos.
- En la declaración de cada método, los productos restituidos son del tipo
AbstractProduct.
ƒ ConcreteFactory (Fábrica concreta): classes TapeDevicesFactory y
CDDevicesFactory.
- Cada una de estas clases Implementa la interfaz de la AbstractFactory (De-
vicesFactory), especificando las operaciones que crean y retornan ob-
jetos correspondientes a productos específicos (ConcreteProduct).
ƒ AbstractProduct: interfaces Media, Recorder y Player.
- Declaran las operaciones que caracterizan a los distintos tipos genéricos de
productos.
ƒ ConcreteProduct: clases Tape, TapeRecorder, TapePlayer, CD, CDRecorder y
CDPlayer.
- Definen los productos creados por cada ConcreteFactory.
Franco Guidi Polanco 4

ƒ Client: clase Client.


- Utiliza la interfaz de la AbstractFactory (DevicesFactory) para acceder a los
métodos de la ConcreteFactory correspondiente a una familia de productos.
- Utiliza los productos a través de su interfaz AbstractProduct.

Descripción del código

En primer lugar, se definen las interfaces de las clases que deberán implementar los
productos análogos pertenecientes a las distintas familias. En el caso de este ejemplo,
Media es la interfaz que implementan los soportes de grabación. Particularmente esta
interfaz no especifica métodos, sólo actúa como una interfaz para “marcar” su rol. Por
su parte, las interfaces Player y Recorder definen las interfaces de reproductores y
grabadores, especificando los distintos métodos con los cuales el cliente interactuará
con ellas.
public interface Media { }

public interface Player {


public void accept( Media med );
public void play( );
}

public interface Recorder {


public void accept( Media med );
public void record( String sound );
}

Los productos de las distintas familias implementan las interfaces definidas anterior-
mente. En el caso de la familia de productos basada en el casete, los productos son
Tape, TapeRecorder y TapePlayer:

public class Tape implements Media {

private String tape= "";

public void saveOnTape( String sound ) {


tape = sound;
}

public String readTape( ) {


return tape;
}

public class TapeRecorder implements Recorder {

Tape tapeInside;

public void accept( Media med ) {


tapeInside = (Tape) med;
}

public void record( String sound ) {


if( tapeInside == null )
System.out.println( "Error: Insert a tape." );
else
tapeInside.saveOnTape( sound );
}

}
Franco Guidi Polanco 5

public class TapePlayer implements Player {

Tape tapeInside;

public void accept( Media med ) {


tapeInside = (Tape) med;
}

public void play( ) {


if( tapeInside == null )
System.out.println( "Error: Insert a tape." );
else
System.out.println( tapeInside.readTape() );
}

Por su parte, los productos pertenecientes a la familia del CD son:


public class CD implements Media{

private String track = "";

public void writeOnDisk( String sound ) {


track = sound;
}

public String readDisk( ) {


return track;
}

public class CDRecorder implements Recorder {

CD cDInside;

public void accept( Media med ) {


cDInside = (CD) med;
}

public void record( String sound ) {


if( cDInside == null )
System.out.println( "Error: No CD." );
else
cDInside.writeOnDisk( sound );
}

public class CDPlayer implements Player {

CD cDInside;

public void accept( Media med ) {


cDInside = (CD) med;
}

public void play( ) {


if( cDInside == null )
System.out.println( "Error: No CD." );
else
System.out.println( cDInside.readDisk() );
}

La interfaz DevicesFactory declara los métodos que utilizará el cliente para interac-
tuar con las fabricas de productos. Nótese que cada método tiene la función de crear
un tipo de producto específico:
public interface DevicesFactory {
Franco Guidi Polanco 6

public Player createPlayer();


public Recorder createRecorder();
public Media createMedia();

Las clases TapeDevicesFactory y CDDevicesFactory corresponden a las fábri-


cas de productos de las diferentes familias. Estas clases corresponden a las Concrete-
Factory para la creación de productos correspondientes a las familias basadas en case-
te en CD, respectivamente.
public class TapeDevicesFactory implements DevicesFactory {

public Player createPlayer() {


return new TapePlayer();
}

public Recorder createRecorder() {


return new TapeRecorder();
}

public Media createMedia() {


return new Tape();
}

public class CDDevicesFactory implements DevicesFactory {

public Player createPlayer() {


return new CDPlayer();
}
public Recorder createRecorder() {
return new CDRecorder();
}
public Media createMedia() {
return new CD();
}

La clase Client es la que finalmente solicita la instanciación de los productos, y los


utiliza. El Client accede tanto a la fábrica de productos, como a los productos mis-
mos a través de sus interfaces comunes. Esto permite al Client utilizar tanto los pro-
ductos basados en casete, como aquellos basados en CD (como también, cualquier
otra familia que adhiera a las interfaces ya declaradas). El Client de este ejemplo
implementa el método selectTechnology recibe una instancia de fábrica, la cual es
utilizada en el interior del método test para crear los productos y utilizarlos. Se debe
notar que la clase Client crea y utiliza los productos sin tener conocimiento acerca
de qué tipo específico de producto está usando.
class Client {

DevicesFactory technology;

public void selectTechnology( DevicesFactory df ) {


technology = df;
}

public void test(String song) {

Media media = technology.createMedia();


Recorder recorder = technology.createRecorder();
Player player = technology.createPlayer();
recorder.accept( media );
System.out.println( "Recording the song : " + song );
recorder.record( song );
System.out.println( "Listening the record:" );
Franco Guidi Polanco 7

player.accept( media );
player.play();
}

Finalmente se presenta la aplicación que crea una instancia de Client, y le asigna


las distintas fábricas de productos para su utilización.

public class AbstractFactoryExample {

public static void main ( String[] arg ) {

Client client = new Client();

System.out.println( “**Testing tape devices” );


client.selectTechnology( new TapeDevicesFactory() );
client.test( "I wanna hold your hand..." );

System.out.println( “**Testing CD devices” );


client.selectTechnology( new CDDevicesFactory() );
client.test( "Fly me to the moon..." );

Observaciones respecto del ejemplo

En este ejemplo se ha querido destacar la necesidad de que el cliente deba crear sólo
productos de una misma familia. En particular se debe notar que los métodos que
ofrece la clase CD y la clase Tape, y que son utilizados por los respectivos grabadores
y reproductores, son distintos. Este hecho, resulta irrelevante al momento de crear los
productos, pues la consistencia de la creación de objetos es garantizada por la clase
fábrica.

Ejecución del ejemplo


C:\Patterns\Creational\Abstract Factory\>java AbstractFactoryExample

**Testing tape devices


Recording the song : I wanna hold your hand...
Listening the record:
I wanna hold your hand...

**Testing CD devices
Recording the song : Fly me to the moon...
Listening the record:
Fly me to the moon...

Observaciones sobre el patrón

Debido a que, tanto la AbstractFactory, como los AbstractProduct de este ejemplo no im-
plementan operaciones, en Java resulta más adecuado codificarlos como interfaces,
en vez de clases abstractas, como se sugiere en [Gamma].

Otros ejemplos

Una compañía que produce videojuegos está interesada en crear un juego en el cual
el usuario debe escoger un personaje que lo representará a lo largo de una aventura,
junto con ciertas herramientas que éste puede utilizar. Las herramientas dependen del
personaje elegido, sin embargo, el código que regula la operación es idéntico para
cualquier personaje.

También podría gustarte