Patrón Método Fábrica

El patrón método fábrica utiliza el concepto del post anterior para definir varias fábricas de objetos. Estas “fábricas” podemos verlas como sucursales o tiendas en diferentes regiones.

La idea con este patrón es tener mayor control sobre nuestras fábricas, ya que, si sólo utilizamos Fábrica Simple, podríamos caer de nuevo en una explosión de fábricas y también de constructores.

¿Qué pasaría si al ejemplo anterior, nos piden crear sucursales por ciudad o por país, y que cada país distribuya su propia Laptop? ¿Cómo nos aseguramos que TODAS las fábricas sigan los estándares adecuados para procesar las Laptops?

¿Se imaginan crear varias clases fábrica por ejemplo LaptopSimpleFactoryCRC, LaptopSimpleFactoryUSA, LaptopSimpleFactoryMEX, etc? El problema con esta propuesta es que cada clase tendría su propia forma de procesar la Laptop, y como hemos visto antes, esto introduce nuevos problemas y hace que el código sea difícil de mantener.

Además, LaptopCenter, tendría que crear un constructor nuevo para recibir cada fábrica.

/**
* Class that handles operations over Laptop class
*/
public class LaptopCenter {
    /**
    * Store the factory instance for CRC
    */
    private LaptopSimpleFactoryCRC laptopFactoryCRC;

    /**
    * Overloaded constructor that receives the specific factory to use
    * @param _laptopFactoryCRC The factory to use to create objects
    */
    public LaptopCenter (LaptopSimpleFactoryCRC _laptopFactoryCRC) {
        // set the factory type
        laptopFactoryCRC = _laptopFactoryCRC;
    }

    /**
    * Store the factory instance for USA
    */
    private LaptopSimpleFactoryUSA laptopFactoryUSA;

    /**
    * Overloaded constructor that receives the specific factory to use
    * @param _laptopFactoryUSA The factory to use to create objects
    */
    public LaptopCenter (LaptopSimpleFactoryUSA _laptopFactoryUSA) {
        // set the factory type
        laptopFactoryUSA = _laptopFactoryUSA;
    }
}

En situaciones como esta, necesitamos un mejor diseño, que nos garantice flexibilidad y además tener un mejor control sobre las diferentes clases. En este caso recordamos el principio que dice “programe en las interfaces, no en las implementaciones” .

Es decir, debemos crear una clase abstracta, que llamaremos LaptopStore, la cual, va a definir el estándar que TODAS las fábricas deberán seguir. Al ser una clase abstracta, si por alguna razón, alguna fábrica necesita sobre escribir algún comportamiento específico, la implementación podrá hacerlo, sin afectar al resto de clases.

En esta nueva clase, vamos a definir un método abstracto (el mismo método de la fábrica simple) createLaptop(). Al definir este método abstracto, estamos obligando a todas las implementaciones (fábricas) a que nos definan cómo se crean las Laptops.

Ahora inclusive podemos ampliar nuestro catálogo de Laptops. Cada sucursal (CRC, USA, MEX, etc) puede tener su propia versión o marca para cada Laptop. Por ejemplo, en CRC se comercializa la marca ASUS, en USA Dell y en MEX ACER.

Diseño inicial de ejemplo - Patrón Método Fábrica
/**
* Class that will be used by other factory classes to handle Laptop instance creation
*/
public abstract class LaptopStore {
    /**
    * @param laptopType The specific laptop to create
    * @return The processed Laptop
    */
    public Laptop processLaptop(LaptopTypeEnum laptopType) {
        Laptop laptop;

        // create the laptop using the factory method
        laptop = createLaptop(laptopType);

        // process the rest of laptop
        review(laptop);
        installOS(laptop);
        pack(laptop);
        destination(laptop);

        // return the laptop
        return laptop;
    }

    /**
    *@param laptopType The specific laptop to create
    *@return The laptop instance
    */
    protected abstract Laptop createLaptop(LaptopTypeEnum laptopType);
}

Nuestro método createLaptop es el encargado de fabricar el tipo de Laptop de manera dinámica. No importa cual sea el tipo de Laptop que sea creado, con el diseño anterior, logramos encapsular y estandarizar cómo se procesan los tipos de Laptop.

Según el libro Head First Design Patterns, el método fábrica debe ser creado de la siguiente manera.

abstract Product factoryMethod(Enum or String type)

El método debe ser abstracto para que las clases que lo implementen definan la manera en que se crean los productos. Los productos deben tener una súper clase, la cual es utilizada de manera general en otros métodos. El método fábrica puede ser parametrizado de ser necesario para poder definir los tipos de producto a crear.

Head First Design Patterns, también nos da una definición oficial para este patrón: El patrón método fábrica define una interfaz para crear un objeto, pero delega a las subclases qué objeto crear. El diagrama sugerido es el siguiente.

Diagrama de clases para el patrón método fábrica

Este patrón nos da otro principio de programación, según Head First Design Patterns:

Dependa de las abstracciones y no de las clases concretas.

El principio anterior se llama Inversión de Dependencias. ¿Cómo logramos aplicar este principio en este ejemplo? Bueno, pues es fácil, si no hubiéramos implementado las fábricas para cada país, la clase que utiliza y necesita las Laptop, tendría varios llamados a las implementaciones de Laptop por medio del new. A eso se le llama dependencia.

Nuestra aplicación estaría dependiendo de manera directa de cada implementación de la clase Laptop, lo cual hace el código difícil de mantener y más propenso a errores.

Al usar fábricas y súper clases abstractas, y encapsular la creación dentro de ellas, estamos invirtiendo la dependencia hacía abstracciones, que es lo que nos define el principio. Ahora, la aplicación utiliza LaptopStore y Laptop para lograr este cometido.

Nuevamente, Head First Design Patterns nos da una guía para asegurarnos de no violar el principio de Inversión de Dependencias:

  • Ninguna variable debería referenciar a una clase concreta: para garantizar este paso, utilizamos fábricas de objetos.
  • Ninguna clase debería heredar de una clase concreta: siempre utilice abstracciones, nunca clases concretas.
  • Ningún método debería de sobre escribir los métodos de la clase base: si se sobrescriben métodos, entonces la abstracción no es del todo correcta.

Esta de más mencionar que no siempre se pueden seguir las reglas anteriores al pie de la letra, sin embargo, es importante siempre tenerlas presentes, mientras diseñamos nuestras aplicaciones.

El resultado de ejecutar nuestra aplicación con dos fábricas (CRC y USA) es el siguiente.

Resultado de ejecutar el código de ejemplo

El código de ejemplo del patrón método fábrica lo pueden encontrar en este link.

En el próximo post vamos a analizar el patrón Fábrica Abstracta.

Recordá suscribirte aquí para recibir las últimas actualizaciones todas las semanas.

Referencias:
Libro Head First Design Patterns. Versión digital.

Leave a Reply

Your email address will not be published. Required fields are marked *