Factoría Abstracta

RECU-0191 (Recurso Patrón)

Descripción

Es un patrón que define una interfaz para crear familias de objetos relacionados o dependientes sin especificar las clases concretas.

Nombre

También es conocido como AbstractFactory, Kit

Clasificación

Patrón creacional

Motivación

Imaginemos la situación de un conjunto de herramientas de interfaces de usuario que soportan varios estándares de representación. En función de cada estándar, el comportamiento y la representación de los elementos de la interfaz varía (scrolls, menu bar, etc..). El cliente no debe cambiar porque cambie la interfaz de usuario. El patrón Factoría Abstracta nos proporciona una solución para esta problemática.

Aplicabilidad

  • Un sistema debe de ser independiente de cómo se crean, componen y representan sus productos.
  • Un sistema debe configurarse con una de entre varias familias de productos.
  • Una familia de productos relacionados están hechos para usarse juntos y se necesita cumplir esa restricción.
  • Se desea ofrecer una biblioteca de clases-producto revelando sus interfaces pero no sus implementaciones.

Estructura

La representación gráfica del modelo es la siguiente:

Participantes

  • Cliente: La clase que llamará a la factoría adecuada ya que necesita crear uno de los objetos que provee la factoría. Es decir, el Cliente lo que quiere es obtener una instancia de alguno de los productos (ProductoA, ProductoB).
  • FactoríaAbstracta: Es de definición de la interfaces de las factorías. Debe de proveer un método para la obtención de cada objeto que pueda crear. ("crearProductoA()" y "crearProductoB()")
  • FactoríasConcretas: Estas son las diferentes familias de productos. Provee de la instancia concreta de la familia que se encarga de crear. De esta forma podemos tener una factoría que cree los elementos gráficos para Windows y otra que los cree para Linux, pudiendo añadir fácilmente (creando una nueva FactoríaConcreta) otra que los cree para MacOS, por ejemplo.
  • ProductoAbstracto: Definición de las interfaces para la familia de productos genéricos. En el diagrama son "ProductoA" y "ProductoB". En un ejemplo de interfaces gráficas podrían ser todos los elementos: Botón, Ventana, Cuadro de Texto, Combo, etc. El Cliente trabajará directamente sobre esta interfaz, que será implementada por los diferentes ProductosConcretos.
  • ProductoConcreto: Implementación de los diferentes productos. Podría ser por ejemplo "BotónWindows" y "BotónLinux". Como ambos implementan "Botón" el Cliente no sabrá si está en Windows o Linux, puesto que trabajará directamente sobre la superclase o interfaz.

Colaboraciones

Normalmente una instancia de una FactoriaConcreta es creada en tiempo de ejecuvión. Esta FactoriaConcreta crea el objeto Producto que tiene una implementacion particular. Para crear diferentes objetos Producto, los clientes deberán usar diferentes FactoriasConcretas.

Consecuencias

  • Aísla al cliente de las clases concretas.
  • Ayuda a controlar la clase de objetos que crea una aplicación.
  • Permite cambiar fácilmente de familia de productos.
  • Promueve la consistencia entre productos, haciendo que una aplicación utilice objetos de una sola familia a la vez
  • La inclusión de nuevos tipos de producto es dificil.

Implementación

Para una correcta implementación del patrón hay que estudiar:

  • Factorías como Singletons: Normalmente una aplicación solo necesita una instancia de cada FactoriaConcreta, por lo tanto, lo más indicado es realizarla como Singleton.
  • Creando Productos: FactoríaAbstracta solo declara una interfaz para la creación de productos. Realmente en las clases de ProductoConcreto es donde se crean los mismos. La forma más sencilla es declarar un método factoría por cada Producto.
  • Definiendo factorias extensibles: FactoríaAbstracta define operaciones por cada producto. La aparición de nuevos productos supone una modificación de la interfaz de FactoríaAbstracta.

Código de ejemplo

Supongamos que disponemos de una cadena de pizzerías. Para crear pizzas disponemos de un método abstracto en la clase Pizzería que será implementada por cada subclase de Pizzería.

abstract Pizza crearPizza()

Concretamente se creará una clase PizzeríaZona por cada zona, por ejemplo la Pizzería de New York sería PizzeriaNewYork y la de Californía PizzeríaCalifornia. Cada pizzería implementará el método con los ingredientes de su zona.

Las pizzas son diferentes según las zonas. No es igual la pizza de New York que la pizza de California. Igualmente, aunque usarán los mismos ingredientes (tomate, mozzarella, etc.) no los obtendrán del mismo lugar, cada zona los comprará donde lo tenga más cerca. Así pues podemos crear un método creador de Pizza que sea

Pizza(FactoriaIngredientes fi);

Como vemos, utilizamos la factoría abstracta (no las concretas de cada zona, como podría ser IngredientesNewYork o IngredientesCalifornia). Pizza podrá obtener los ingredientes de la factoría independientemente de donde sea. Sería fácil crear nuevas factorías y añadirlas al sistema para crear pizzas con estos nuevos ingredientes. Efectivamente, en este ejemplo el cliente es Pizza y es independiente de la Factoría usada.

El creador de la Pizza será el encargado de instanciar la factoría concreta, así pues los encargados de instanciar las factorías concretas serán las pizzerías locales. En PizzeríaNewYork podemos tener el método crearPizza() que realice el siguiente trabajo:

Pizza crearPizza() {
    FactoríaIngredientes fi = new IngredientesNewYork();
    Pizza pizza = new Pizza(fi); // Uso de la factoría
    pizza.cortar();
    pizza.empaquetar();
    return pizza;
}

Usos conocidos

Cualquier cadena industrial funciona bajo este patrón.

Patrones relacionados

  • Singleton: Las FactoríasConcretas suelen ser a menudo Singleto
  • Factoría: A menudo Clases de FactoriaAbstracta suelen implementarse con métodos de Factoría

Contenidos relacionados

Recursos
Área: Desarrollo » Patrones de Diseño
Código Título Tipo Carácter
RECU-0013 Patrones de diseño Ficha Recomendado
RECU-0202 Singleton Patrón Recomendado
RECU-0190 Factoría Patrón Recomendado