Factory Pattern: Crea Objetos de Forma Flexible sin Especificar Clases Exactas
Aprende a implementar el patrón Factory para crear objetos de forma flexible, desacoplando el código que crea objetos del código que los usa.
TL;DR - Resumen rápido
- El Factory Pattern crea objetos sin especificar sus clases exactas
- Desacopla el código que crea objetos del código que los usa
- Se puede implementar con funciones simples o clases factory
- Facilita la creación de objetos complejos con múltiples pasos
- Permite agregar nuevos tipos de objetos sin modificar el código existente
Introducción
El Factory Pattern es un patrón de diseño creacional que proporciona una interfaz para crear objetos en una superclase, pero permite a las subclases alterar el tipo de objetos que se crearán. En JavaScript, este patrón es especialmente útil porque el lenguaje no tiene una forma nativa de implementar factories, aunque las características modernas como las clases ES6 facilitan su implementación.
El Factory Pattern resuelve el problema de tener que especificar la clase exacta de un objeto que quieres crear. En lugar de usar `new` con una clase específica, usas una factory que decide qué objeto crear basándose en ciertos criterios. Esto desacopla el código que crea objetos del código que los usa, lo que facilita la mantenibilidad y la extensibilidad del código.
Contexto del Patrón
El Factory Pattern fue formalizado por la "Gang of Four" en su libro de 1994 "Design Patterns: Elements of Reusable Object-Oriented Software". Es uno de los patrones más utilizados en desarrollo de software porque resuelve un problema muy común: la creación de objetos de forma flexible y desacoplada.
¿Qué es el Factory Pattern?
El Factory Pattern es un patrón de diseño creacional que define una interfaz para crear un objeto, pero deja que las subclases decidan qué clase instanciar. El patrón Factory permite a una clase delegar la instanciación a las subclases. En JavaScript, esto se puede implementar de varias formas, desde funciones simples hasta clases factory más complejas.
La idea principal del Factory Pattern es encapsular la lógica de creación de objetos. En lugar de crear objetos directamente usando `new`, usas una factory que se encarga de decidir qué objeto crear y cómo inicializarlo. Esto facilita cambiar la implementación sin afectar el código que usa los objetos, y permite agregar nuevos tipos de objetos sin modificar el código existente.
Implementación con Función
La implementación más simple del Factory Pattern en JavaScript usa una función que crea y retorna objetos basándose en parámetros de entrada. Esta implementación es ideal para casos simples donde no necesitas la complejidad de una clase factory.
Este ejemplo muestra una factory simple que crea diferentes tipos de productos basándose en el parámetro `tipo`. La función `crearProducto` decide qué objeto crear y retorna una instancia con las propiedades apropiadas. El código que usa la factory no necesita conocer las clases internas, solo la interfaz común que todos los productos implementan.
Ventaja Clave
Las factory functions son simples y fáciles de entender. No requieren clases ni herencia, lo que las hace ideales para proyectos pequeños o situaciones donde no necesitas la complejidad de una factory más avanzada.
Implementación con Clase
Para casos más complejos, puedes implementar el Factory Pattern usando una clase factory. Esta implementación es más estructurada y permite agregar lógica adicional como validación, logging o caching de instancias.
Este ejemplo muestra una factory implementada como una clase ES6. La clase `VehiculoFactory` tiene un método `crear` que decide qué tipo de vehículo crear basándose en el parámetro `tipo`. La factory también puede incluir lógica adicional como validación de parámetros o registro de instancias creadas.
Factory Abstracta
La Factory Abstracta es una variación del Factory Pattern que proporciona una interfaz para crear familias de objetos relacionados sin especificar sus clases concretas. Este patrón es útil cuando necesitas crear múltiples tipos de objetos relacionados que deben ser consistentes entre sí.
Este ejemplo muestra una Factory Abstracta que crea familias de objetos relacionados (botones y ventanas) para diferentes estilos (claro y oscuro). La `GUIFactory` define la interfaz para crear estos objetos, y las implementaciones concretas (`FactoryClaro` y `FactoryOscuro`) crean objetos que son consistentes entre sí.
Ventajas del Factory Pattern
El Factory Pattern ofrece varias ventajas importantes que lo hacen adecuado para ciertos tipos de problemas:
- Desacopla el código que crea objetos del código que los usa
- Facilita agregar nuevos tipos de objetos sin modificar el código existente
- Encapsula la lógica de creación de objetos complejos
- Permite reutilizar código de inicialización de objetos
- Facilita el testing al poder mockear las factories
- Centraliza la lógica de creación en un solo lugar
Un ejemplo práctico que muestra las ventajas del Factory Pattern es la creación de notificaciones de diferentes tipos:
Este ejemplo muestra cómo el Factory Pattern facilita la creación de notificaciones de diferentes tipos. La factory `NotificacionFactory` encapsula la lógica de creación de cada tipo de notificación, y el código que usa las notificaciones no necesita conocer los detalles de implementación.
Casos de Uso
El Factory Pattern es ideal en situaciones donde necesitas crear objetos de forma flexible y desacoplada. Aquí están los casos de uso más comunes:
- Creación de objetos complejos con múltiples pasos de inicialización
- Sistemas donde el tipo de objeto a crear se decide en tiempo de ejecución
- Librerías y frameworks que necesitan crear objetos sin conocer sus clases
- Aplicaciones que soportan múltiples formatos o protocolos
- Sistemas de logging con diferentes tipos de salidas
- Creación de componentes UI con diferentes estilos o temas
Un caso de uso práctico es crear una factory para diferentes tipos de usuarios en un sistema:
Esta factory de usuarios demuestra cómo el Factory Pattern facilita la creación de diferentes tipos de usuarios con permisos y comportamientos diferentes. La factory encapsula la lógica de creación de cada tipo de usuario, y el código que usa los usuarios no necesita conocer los detalles de implementación.
Errores Comunes
Al usar el Factory Pattern, hay varios errores que los desarrolladores cometen frecuentemente. Conocer estos errores te ayudará a evitar problemas y escribir código más robusto.
Factory Dios
Un error común es crear una factory que hace demasiado, convirtiéndose en una "God Factory" que conoce todos los detalles de creación de todos los objetos. Esto viola el principio de responsabilidad única y hace el código difícil de mantener.
En este ejemplo, la `AppFactory` es una "God Factory" que conoce todos los detalles de creación de todos los objetos de la aplicación. Esto hace el código difícil de mantener y extender. La solución es dividir la factory en múltiples factories más pequeñas, cada una responsable de un tipo específico de objetos.
Advertencia Importante
Las "God Factories" violan el principio de responsabilidad única y hacen el código difícil de mantener. Divide las factories en unidades más pequeñas, cada una responsable de crear un tipo específico de objetos.
Sin Interfaz Común
Otro error común es crear objetos que no comparten una interfaz común, lo que dificulta el uso polimórfico de los objetos creados por la factory. El Factory Pattern funciona mejor cuando todos los objetos creados implementan la misma interfaz.
En este ejemplo, los objetos creados por la factory no comparten una interfaz común. `Auto` tiene un método `conducir`, `Avion` tiene un método `volar` y `Barco` tiene un método `navegar`. Esto dificulta el uso polimórfico de los objetos. La solución es definir una interfaz común que todos los objetos implementen.
Factory Estática vs Instanciable
Un error conceptual es usar factories estáticas cuando necesitas mantener estado o configuración. Las factories estáticas son útiles para casos simples, pero si necesitas mantener configuración o estado, debes usar factories instanciables.
En este ejemplo, la `FactoryEstatica` no puede mantener configuración entre llamadas porque es estática. Cada llamada a `crear` usa la misma configuración predeterminada. La solución es usar una factory instanciable que pueda mantener su propio estado y configuración.
Resumen: Factory Pattern
Conceptos principales:
- •El Factory Pattern crea objetos sin especificar sus clases exactas
- •Desacopla el código que crea objetos del código que los usa
- •Se puede implementar con funciones simples o clases factory
- •Facilita agregar nuevos tipos de objetos sin modificar el código existente
- •Encapsula la lógica de creación de objetos complejos
Mejores prácticas:
- •Usar factories simples para casos simples, clases factory para casos complejos
- •Definir una interfaz común para todos los objetos creados
- •Evitar factories que hacen demasiado (responsabilidad única)
- •Considerar factories instanciables cuando necesitas mantener estado
- •Documentar claramente qué objetos crea cada factory