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

Grabador

CD

Cinta magnética

Reproductor

Reproductor

Fig. x.1
Si estos productos ofrecieran a sus usuarios la misma interfaz, un cliente podría realizar el mismo proceso de prueba de productos en cualquiera de las familias. Por ejemplo, podría registrar una canción utilizando el dispositivo de grabación, para luego escucharlo en el dispositivo de reproducción. Sólo deberíamos asegurarnos que los productos 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 integrantes 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álogos pertenecientes a diferentes familias, permitirá a un mismo cliente realizar operaciones similares sobre artefactos de tecnologías distintas. El problema surge cuando el
cliente debe manejar familias cuyos productos no deben entremezclarse. Por ejemplo,

2 . no con una cinta magnética o un reproductor de DVD. Estructura del patrón de diseño <<stereotipe>> Client <<stereotipe>> <<stereotipe>> AbstractFactory AbstractProduct {abstract} {abstract} +createProduct() : AbstractProduct {abstract} <<stereotipe>> ConcreteFactory <<stereotipe>> <<instantiates>> produces ConcreteProduct #ConcreteProduct() +createProduct() : AbstractProduct Fig. De esta forma. Por su parte. En el caso del ejemplo. La diferencia radica en que. 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. Los neófitos en el campo de los patrones de diseño suelen cuestionarse la diferencia entre este patrón y el Factory Method. la encapsulación de los productos tras interfaces comunes permitirá al cliente utilizar productos análogos pertenecientes a familias diferentes.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. quedando absolutamente desligado de la implementación particular de estos últimos. mientras el Factory Method tiene por objetivo diferir hacia una determinada clase (o subclase) el tipo de producto a instanciar. el cliente no deberá invocar en caso alguno el constructor de los productos. el patrón Abstract Factory persigue garantizar la creación de un conjunto de productos relacionados. la interfaz de las fábricas permitirá al cliente solicitar la generación de instancias de grabador. puesto que ambos presentan una estructura similar. x. y que permitirá al cliente interactuar con ellas. Ésta permite ocultar en una clase “fábrica” el proceso de instanciación de un conjunto de productos. Esta interfaz proveerá los métodos cuyo contrato consistirá en retornar instancias de cada tipo de producto dentro de una familia. de medios de registro y de reproductores. al encapsular en una clase el proceso de instanciación de un objeto. El patrón de diseño Abstract Factory resuelve este problema por medio de la encapsulación de reglas de instanciación. 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.

- ƒ concreta): Cada una de estas clases Implementa la interfaz de la AbstractFactory (DevicesFactory). En la declaración de cada método. ConcreteProduct: clases Tape. CD. TapePlayer. - ƒ Declara una interfaz para las operaciones que crean y restituyen productos.3 Participantes ƒ AbstractFactory (Fábrica Abstracta): interfaz DevicesFactory. los productos restituidos son del tipo AbstractProduct. TapeRecorder. . x. CDRecorder y CDPlayer. Declaran las operaciones que caracterizan a los distintos tipos genéricos de productos. - ƒ ConcreteFactory (Fábrica CDDevicesFactory. classes TapeDevicesFactory y AbstractProduct: interfaces Media. especificando las operaciones que crean y retornan objetos correspondientes a productos específicos (ConcreteProduct).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 Fact ory Tape Devices Factory Tape CD + saveOnTape(String sound) + readTape() : String + writeOnDisk(String sound) + readDisk() : String <<Interface>> Recorder + accept(Media) + record() Tape Recorder CD Recorder <<Interface>> Player + ac cept(Media) + play() Tape Player CD Player Fig. - Definen los productos creados por cada ConcreteFactory. Recorder y Player.

public void accept( Media med ) { tapeInside = (Tape) med.Franco Guidi Polanco 4 Client: clase Client. } } . public void saveOnTape( String sound ) { tape = sound. else tapeInside. ƒ - 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. sólo actúa como una interfaz para “marcar” su rol. 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. } Los productos de las distintas familias implementan las interfaces definidas anteriormente. public void play( ). public interface Media { } public interface Player { public void accept( Media med ).println( "Error: Insert a tape. } } public class TapeRecorder implements Recorder { Tape tapeInside." ). En el caso de este ejemplo. En el caso de la familia de productos basada en el casete.out. public void record( String sound ). Por su parte. Particularmente esta interfaz no especifica métodos. se definen las interfaces de las clases que deberán implementar los productos análogos pertenecientes a las distintas familias. Descripción del código En primer lugar.saveOnTape( sound ). } public interface Recorder { public void accept( Media med ). los productos son Tape. TapeRecorder y TapePlayer: public class Tape implements Media { private String tape= "". } public String readTape( ) { return tape. } public void record( String sound ) { if( tapeInside == null ) System. Media es la interfaz que implementan los soportes de grabación.

readDisk() ). public void accept( Media med ) { cDInside = (CD) med." )." ). else System.out.out. } public void record( String sound ) { if( cDInside == null ) System. Nótese que cada método tiene la función de crear un tipo de producto específico: public interface DevicesFactory { .println( "Error: No CD.writeOnDisk( sound ). else System. } } La interfaz DevicesFactory declara los métodos que utilizará el cliente para interactuar con las fabricas de productos.println( "Error: No CD. } public String readDisk( ) { return track. } } Por su parte. else cDInside. public void writeOnDisk( String sound ) { track = sound. public void accept( Media med ) { tapeInside = (Tape) med.Franco Guidi Polanco 5 public class TapePlayer implements Player { Tape tapeInside.readTape() ). } public void play( ) { if( cDInside == null ) System. los productos pertenecientes a la familia del CD son: public class CD implements Media{ private String track = "". } public void play( ) { if( tapeInside == null ) System.println( cDInside.println( tapeInside.println( "Error: Insert a tape.out. } } public class CDRecorder implements Recorder { CD cDInside.out. } } public class CDPlayer implements Player { CD cDInside.out." ). public void accept( Media med ) { cDInside = (CD) med.

out.createRecorder(). } public Media createMedia() { return new Tape().out.record( song ).createPlayer(). respectivamente. Recorder recorder = technology. } public void test(String song) { Media media = technology. class Client { DevicesFactory technology. System. . System. Esto permite al Client utilizar tanto los productos basados en casete. Estas clases corresponden a las ConcreteFactory para la creación de productos correspondientes a las familias basadas en casete en CD. como aquellos basados en CD (como también.accept( media ). cualquier otra familia que adhiera a las interfaces ya declaradas).println( "Recording the song : " + song ). como a los productos mismos a través de sus interfaces comunes. la cual es utilizada en el interior del método test para crear los productos y utilizarlos. public void selectTechnology( DevicesFactory df ) { technology = df. 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. El Client accede tanto a la fábrica de productos. public class TapeDevicesFactory implements DevicesFactory { public Player createPlayer() { return new TapePlayer(). public Recorder createRecorder(). } } La clase Client es la que finalmente solicita la instanciación de los productos. recorder. y los utiliza.println( "Listening the record:" ).createMedia().Franco Guidi Polanco 6 public Player createPlayer(). Player player = technology. El Client de este ejemplo implementa el método selectTechnology recibe una instancia de fábrica. } public Recorder createRecorder() { return new CDRecorder(). } public Media createMedia() { return new CD(). } public Recorder createRecorder() { return new TapeRecorder(). } Las clases TapeDevicesFactory y CDDevicesFactory corresponden a las fábricas de productos de las diferentes familias. recorder. } } public class CDDevicesFactory implements DevicesFactory { public Player createPlayer() { return new CDPlayer(). public Media createMedia().

junto con ciertas herramientas que éste puede utilizar.. } } Finalmente se presenta la aplicación que crea una instancia de Client. Este hecho.. en vez de clases abstractas.selectTechnology( new CDDevicesFactory() ).accept( media ).." ).test( "Fly me to the moon. sin embargo. } } 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.out... Observaciones sobre el patrón Debido a que. **Testing Recording Listening Fly me to CD devices the song : Fly me to the moon. the record: the moon.selectTechnology( new TapeDevicesFactory() ).Franco Guidi Polanco 7 player. public class AbstractFactoryExample { public static void main ( String[] arg ) { Client client = new Client()....test( "I wanna hold your hand.. como se sugiere en [Gamma].. System. client.play(). Las herramientas dependen del personaje elegido. el código que regula la operación es idéntico para cualquier personaje.. 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. . client. System. 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. client. client.println( “**Testing CD devices” )." ). resulta irrelevante al momento de crear los productos. como los AbstractProduct de este ejemplo no implementan operaciones.. tanto la AbstractFactory. pues la consistencia de la creación de objetos es garantizada por la clase fábrica.println( “**Testing tape devices” ).out. y le asigna las distintas fábricas de productos para su utilización. en Java resulta más adecuado codificarlos como interfaces. Ejecución del ejemplo C:\Patterns\Creational\Abstract Factory###BOT_TEXT###gt;java AbstractFactoryExample **Testing tape devices Recording the song : I wanna hold your hand. Listening the record: I wanna hold your hand. player. son distintos.