martes, 2 de febrero de 2010

Patrones de diseño: Factorías

Una factoría es un objeto que maneja la creación de otros objetos. Las factorías se utilizan cuando la creación de un objeto implica algo más que una simple instanciación.
Los siguientes ejemplos son casos donde una factoría puede ayudar:
  • Es necesario acceder a algún recurso para la creación y configuración de un objeto.
  • No conocemos hasta el momento preciso de la instanciación qué tipo concreto de objeto se va a instanciar.

En estos casos, en lugar de que sea el propio objeto quien se encargue de todos los aspectos realativos a la creación, se crea otro objeto que lo haga. De esta manera, se libera al objeto que va a ser creado de aquellas responsabilidades que no le corresponden pero que son necesarias para su creación, manteniendo su independencia.

El siguiente es un ejemplo de la utilización de una factoría para la creación de objetos. Se pueden crear figuras geométricas y calcular su área. Las figuras estarían definidas por una interfaz. Las figuras posibles son círculos y cuadrados.

Se va a utilizar una factoría para crear las figuras, puesto que no conocemos de antemano a qué clase pertenece el objetoque tenemos que instanciar. El uso de una factoría permite separar la lógica de negocio, igual para todos los casos, de la lógica de instanciación de los objetos. De este forma el código queda más claro.
public interface Figura {
public double getArea();
}

public class Circulo implements Figura {
double radio;

public Circulo(double radio) {
this.radio = radio;
}

public double getArea() {
return (3.14 * radio * radio);
}
}

public class Cuadrado implements Figura {
double lado;

public Cuadrado(double lado) {
this.lado = lado;
}

public double getArea() {
return lado * lado;
}
}

public class Principal {
public static void main(String[] args) {
int tipo = Integer.parseInt(args[0]);
double lado = Double.parseDouble(args[0]);

Figura figura =
FiguraFactory.getFigura(tipo, lado);

System.out.println("El area de la figura es: " + figura.getArea());
}
}

public class FiguraFactory {
public final static int CUADRADO = 0;
public final static int CIRCULO = 1;

public static Figura getFigura(int tipo, double lado) {
switch (tipo) {
case CUADRADO:
return new Cuadrado(lado);
case CIRCULO:
return new Circulo(lado);
}
return null;
}
}


El ejemplo anterior se corresponde con el patrón de diseño Factory Method, que se refiere a la utiliación de un método cuyo propósito principal es la creación de objetos.

Existe otro patrón de diseño denominado Abstract Factory, que proporciona una interfaz para crear familias de objetos que dependen entre sí, sin especificar sus clases concretas.

El siguiente es un ejemplo de una factoría abstracta sacado de la Wikipedia, en el que se crea un botón que puede pertenecer a dos familias distintas de componentes gráficos y la aplicación no sabe hasta el momento de la ejecución a qué familia pertenecerá este botón:


interface GUIFactory {
public Button createButton();
}

class WinFactory implements GUIFactory {
public Button createButton() {
return new WinButton();
}
}

class OSXFactory implements GUIFactory {
public Button createButton() {
return new OSXButton();
}
}

interface Button {
public void paint();
}

class WinButton implements Button {
public void paint() {
System.out.println("I'm a WinButton");
}
}

class OSXButton implements Button {
public void paint() {
System.out.println("I'm an OSXButton");
}
}

class Application {
public Application(GUIFactory factory){
Button button = factory.createButton();
button.paint();
}
}

public class ApplicationRunner {
public static void main(String[] args) {
new Application(createOsSpecificFactory());
}

public static GUIFactory createOsSpecificFactory() {
int sys = readFromConfigFile("OS_TYPE");
if (sys == 0) {
return new WinFactory();
} else {
return new OSXFactory();
}
}
}

Referencias:
Objeto Factoría en Wikipedia
Patrón Factoría en Wikipedia
Patrón Factory Method en Wikipedia
Factory Pattern - Patrones
Patrón Abstract Factory en Wikipedia
Abstract Factory Pattern - Patrones
Otros patrones de diseño


5 comentarios:

  1. Saludos, al principio cuando presentas los casos de usos

    " * Es necesario acceder a algún recurso para la creación y configuración de un objeto.
    * No conocemos hasta el momento preciso de la instanciación qué tipo concreto de objeto se va a instanciar."

    Cuando se dan esos casos? dame alguno ejemplos si puesdes de paso.

    gracias de antemano.

    ResponderEliminar
  2. Gracias a tu blog, entendi perfectamente el uso de Factory Method.

    Ahora bien, no logre entender bien el uso del Abstract Factory. Me lo podrias explicar?

    ResponderEliminar
  3. Hola JorWan,

    El punto clave en el patrón Abstract Factory es que, como su propio nombre indica lo que se crea es una factoría abstracta. Es decir, se crea una clase abstracta o una interface que la defina, como en el ejemplo, y se crean implementaciones concretas de esta clase abstracta que permiten la creación de familias distintas de objetos. En el ejemplo se crean factorias concretas de GUIs para Windows y para OSX.

    En cuanto a los casos de uso de las factorías, te pongo estos ejemplos:

    - Es necesario acceder a algún recurso para la creación y configuración de un objeto: Por ejemplo, objetos que al inicializarse recogen su configuración inicial desde un fichero XML. Si se centraliza su creación en una factoría, la lectura de este XML se realiza en un punto único y no se duplica código en la aplicación.

    - No conocemos hasta el momento preciso de la instanciación qué tipo concreto de objeto se va a instanciar: Podemos tener una aplicación que maneja figuras geométricas, pero es el usuario de la aplicación quién elige si necesita un triángulo o un cuadrado. Me diante el uso de una factoría independizamos la aplicación de la creación de los objetos que decida el usuario.

    Espero que te sirva de ayuda.

    Un saludo.

    ResponderEliminar
  4. Entendido, gracias por la ayuda

    ResponderEliminar
  5. Muchas gracias, ha quedado bastante claro..aunque en el tema grafico me pierdo un poco jejje

    Por cierto hay un pequeño error no :
    int tipo = Integer.parseInt(args[0]);
    double lado = Double.parseDouble(args[0]);

    se deberia utilitzar un parametro diferente, seria lo logico y asi:

    int tipo = Integer.parseInt(args[0]);
    double lado = Double.parseDouble(args[1]);

    ResponderEliminar