Factory Method

Daniel Cataño Restrepo
Analista de Desarrollo

¿Qué es?, ¿Qué problema resuelve?

Factory Method o simplemente Factory, es un patrón de diseño creacional que se usa para crear implementaciones concretas de una interfaz común. Por ejemplo, una aplicación requiere que un objeto con una interfaz específica realice sus tareas. La implementación concreta de la interfaz se identifica por un parámetro. En lugar de crear una estructura compleja de condicionales (if/elseif/else) para determinar la implementación concreta, la aplicación delega esa decisión a un componente, aparte, que crea un objeto concreto. Con este acercamiento, el código de la aplicación se simplifica, haciéndolo reusable y más fácil de mantener.

Diagrama

Imagen 1
  • Product: Define la interfaz de objetos que el FactoryMethod crea.
  • ConcreteProduct: Implementa la interfaz Product.
  • Creator: Declara el FactoryMethod, el cual retorna un objeto del tipo Product. El Creator también puede definir un método por defecto de implementación.
  • ConcreteCreator: Sobrescribe FactoryMethod para retornar una instancia de ConcreteProduct.

Ejemplo (código antes y código después)

Imagen 2

En este ejemplo se tiene una clase que convierte un objeto Instance en la representación de un XML, JSON o YML. Dependiendo del valor que tome format hace la conversión correspondiente.

Un código que usa estructuras condicionales para cambiar el comportamiento de una aplicación es más complejo de mantener y de entender. Es difícil de mantener porque es un código que hace muchas cosas y, de acuerdo al principio de responsabilidad única, se espera que un método tenga una única responsabilidad. 

El método serialize puede cambiar debido a:

  • La necesidad de agregar un nuevo formato.
  • Cuando Instance cambie.
  • Cuando la representación de un formato cambie.
  • Lo ideal sería que si uno de esos cambios ocurre, estos se puedan implementar sin cambiar este método.

Cómo aplicar Factory:

Lo primero que se debe revisar cuando se tiene una estructura condicional compleja, es identificar el fin común en cada uno de los posibles caminos de ejecución. Normalmente, en estas estructuras existe un objetivo común que se logra de diferentes maneras. En este ejemplo, el objetivo es convertir una instancia en un string que representa un formato. Para aplicar Factory, se siguen los siguientes pasos:

  • Se crea una interfaz que contiene los objetivos comunes. En este caso convertir una instancia (Product).
Imagen 3

Se crean implementaciones diferentes por cada ruta lógica, es decir, por cada if/else, etc… (ConcreteCreator). Por cada implementación se obtiene un ConcreteProduct. En este caso se crea una implementación para serializar a XML y otra para JSON.

Es importante tener en cuenta que estos métodos retornan estos valores por motivos ilustrativos. En estas implementaciones iría toda la lógica necesaria para convertir un objeto en un string que representa el formato seleccionado. Por ejemplo, utilizar la librería Gson para convertir el objeto a JSON.

Imagen 4
Imagen 5
  • Se crea un componente aparte que decide qué implementación usar basado en un formato específico (Creator). En este caso si el formato no es soportado se retorna null, sin embargo se puede lanzar una excepción o bien crear otra implementación que maneje el caso por defecto.
Imagen 6

El programa cliente simplemente llama al director con el formato esperado y puede hacer uso del método serialize para obtener la respuesta. Todo esto, de una forma transparente, para el código cliente.En la imagen 7 podemos ver cómo al enviar el formato JSON se obtiene foo; esto es lo que devuelve la implementación para JSON.

Imagen 7
  • Finalmente, si es necesario agregar otro formato, se agrega esta nueva opción al director. Nótese en la imagen 8 cómo se utiliza un Enum en lugar de un String; con esto se busca limitar al código cliente a enviar solo datos que estén dentro del enum.
Imagen 8

Y se crea una implementación concreta para soportar YML.

Imagen 8

El código de ejemplo se puede encontrar en Aquí

Conclusiones:

  • El patrón es útil cuando se tienen diferentes implementaciones de una misma interfaz.
  • Factory separa lógica compleja que es difícil de mantener en partes atómicas.
  • Es flexible porque se pueden agregar nuevos tipos sin romper los ya existentes.

Fuentes:

Real Python

UML