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.
