El patrón Factory es un patrón de diseño creacional ampliamente utilizado en Java. Sin embargo, su implementación clásica presenta un defecto importante: infringe el principio de Open/Closed del diseño SOLID. Cada vez que se necesita añadir un nuevo tipo e implementación, la clase Factory debe modificarse.
Presentamos Java 8, con sus potentes expresiones lambda y la interfaz funcional Supplier
. Esto nos permite crear un Patrón de Fábrica Dinámica más flexible y extensible, manteniendo nuestra clase de fábrica cerrada a modificaciones y abierta a extensiones.
The Problem with Classic Factory Pattern
Comencemos con un ejemplo utilizando el patrón Factory clásico.
interface Shape { void draw(); } class Circle implements Shape { public void draw() { System.out.println("Drawing a Circle"); } } class Square implements Shape { public void draw() { System.out.println("Drawing a Square"); } } class ShapeFactory { public static Shape createShape(String type) { switch (type) { case "circle": return new Circle(); case "square": return new Square(); default: throw new IllegalArgumentException("Unknown shape: " + type); } } }
Desventajas
- Violación del principio Open/Closed: Cada nueva implementación required de mofidificar la clase Factory, en este caso
ShapeFactory
. - Difícil de escalar: Con el tiempo, la fábrica se convierte en una gran central monolítica.
- Díficil de probar y mantener.
Factory dinámica con Lambdas y Supplier
Java 8 introdujo la interfaz Supplier
, ideal para las fábricas. Representa una función que proporciona una instancia del tipo T. Vamos a ver como construir una fábrica dinámica que permita el registro de nuevos tipos en tiempo de ejecución, evitando así cambios en la propia fábrica.
1. Definir la interface y sus implementaciones
interface Shape { void draw(); } class Circle implements Shape { public void draw() { System.out.println("Drawing a Circle"); } } class Square implements Shape { public void draw() { System.out.println("Drawing a Square"); } }
2. Crear el Dynamic Factory
import java.util.HashMap; import java.util.Map; import java.util.function.Supplier; class DynamicShapeFactory { private final Map<String, Supplier<Shape>> registry = new HashMap<>(); public void registerShape(String shapeName, Supplier<Shape> supplier) { registry.put(shapeName.toLowerCase(), supplier); } public Shape createShape(String shapeName) { Supplier<Shape> supplier = registry.get(shapeName.toLowerCase()); if (supplier != null) { return supplier.get(); } throw new IllegalArgumentException("Unknown shape: " + shapeName); } }
3. Ejemplo de Uso
public class Main { public static void main(String[] args) { DynamicShapeFactory factory = new DynamicShapeFactory(); // Register shapes factory.registerShape("circle", Circle::new); factory.registerShape("square", Square::new); // Create shapes dynamically Shape shape1 = factory.createShape("circle"); Shape shape2 = factory.createShape("square"); shape1.draw(); // Output: Drawing a Circle shape2.draw(); // Output: Drawing a Square } }
Beneficios del Patrón Factory dinámico
- Se adhiere al principio Open/Closed: se pueden agregar nuevas formas sin modificar
DynamicShapeFactory
. - Extensible en tiempo de ejecución: puede cargar y registrar nuevos tipos incluso a través de archivos de configuración o inyección de dependencia.
- Más fácil de probar: puedes simular o crear fábricas provisionales proporcionando proveedores personalizados en escenarios de prueba.
Conclusiones
El Patrón de Fábrica Dinámica en Java, habilitado por lambdas y Supplier
, es una alternativa limpia, elegante y moderna al enfoque de fábrica clásico. Favorece una mejor arquitectura de software al reducir el acoplamiento, aumentar la flexibilidad y adoptar los principios SOLID.
Si bien puede introducir una ligera complejidad inicial debido a la lógica de registro, los beneficios a largo plazo, especialmente en aplicaciones escalables y modulares, lo convierten en una poderosa opción de diseño.