Ya conoces los cinco patrones creacionales por separado. Esta última lección del módulo los pone sobre la misma mesa, que es donde se toman las decisiones reales: rara vez la pregunta es "¿cómo funciona Builder?" sino "¿qué uso aquí: un builder, una fábrica, o nada?". Compararemos intenciones y costes, te daré una guía de decisión en forma de diagrama, veremos cómo se combinan entre sí (en el código real casi nunca aparecen solos) y cuál es su evolución típica en la vida de un proyecto. Cerraremos con el mapa creacional completo de PideYa: qué patrón quedó instalado en cada parte del sistema y por qué.
Contenido
- Los cinco, frente a frente
- Guía de decisión
- Cómo se combinan entre sí
- La evolución típica: los patrones se ganan
- El mapa creacional de PideYa
- Errores comunes, ejercicios y conclusión
Los cinco, frente a frente
La tabla que conviene tener interiorizada (las columnas de coste retoman la balanza de la lección 01-06):
| Patrón | Intención en una frase | Úsalo cuando... | Coste principal |
|---|---|---|---|
| Singleton | Una sola instancia, con acceso global | Un recurso técnico debe ser único y compartido (y no hay contenedor DI que lo gestione) | Estado global: dependencias ocultas, tests frágiles; casi siempre es mejor inyectar |
| Factory Method | Un método decide qué clase concreta instanciar; las variantes lo redefinen o se inyectan | La clase del producto varía y quieres añadir variantes sin tocar el código que las usa | Una pareja creador/producto por variante: jerarquía o registro que mantener |
| Abstract Factory | Un objeto fabrica una familia coherente de productos | Varias familias intercambiables cuyas piezas no deben mezclarse (mercados de PideYa) | Matriz de clases (variantes × productos); añadir un producto rompe todas las fábricas |
| Builder | Construir paso a paso un objeto complejo, validando al final | Muchos parámetros/opcionales, validación cruzada, inmutabilidad deseada | Una clase builder por producto; ceremonia excesiva para objetos pequeños |
| Prototype | Crear copiando un ejemplar existente | El punto de partida natural es un objeto ya configurado (repetir, plantillas) | Razonar la política de copia (superficial/profunda) campo a campo |
Dos ejes transversales que ayudan a no confundirlos:
- ¿Dónde está la dificultad? En elegir la clase → fábricas (una pieza: Factory Method; familia coherente: Abstract Factory). En montar el objeto → Builder. En el punto de partida (ya existe uno igual) → Prototype. En cuántas instancias → Singleton.
- ¿Qué se le da al mecanismo? A una fábrica se le dan parámetros y decide la clase; a un builder se le dan datos poco a poco y monta; a un prototipo no se le da nada: ya contiene su estado.
Guía de decisión
El flujo de preguntas que resume el módulo. Como toda guía, orienta el 90% de los casos; el 10% restante es criterio (y recuerda el filtro previo a todos: ¿de verdad duele el new directo?):
flowchart TD
A[Necesito crear un objeto] --> B{¿El 'new' directo<br/>ya duele?<br/>síntomas de 02-01}
B -- No --> Z[new directo o simple factory<br/>y a otra cosa: YAGNI]
B -- Sí --> C{¿El problema es<br/>CUÁNTAS instancias?}
C -- "Debe haber solo una" --> D[¿Hay contenedor DI?]
D -- Sí --> D1[Ámbito singleton<br/>del contenedor]
D -- No --> D2[Singleton<br/>holder idiom o enum]
C -- No --> E{¿Existe ya un ejemplar<br/>que sea el mejor plano?}
E -- Sí --> F[Prototype<br/>+ registro si hay catálogo]
E -- No --> G{¿La dificultad está en<br/>el MONTAJE?<br/>muchos opcionales, validación}
G -- Sí --> H[Builder<br/>variante fluida]
G -- No --> I{¿Varias piezas que deben<br/>ser COHERENTES entre sí?}
I -- Sí --> J[Abstract Factory]
I -- No --> K[Factory Method<br/>o registro de Suppliers]
Tres consejos de uso de la guía:
- Las respuestas pueden ser varias a la vez: un pedido complejo (Builder) cuya pasarela de cobro elige una fábrica de mercado (Abstract Factory) es lo normal, no la excepción. La guía se aplica por decisión de creación, no por sistema.
- Cuando dudes entre Factory Method y Abstract Factory, la pregunta discriminante es siempre la misma: ¿hay invariante de coherencia entre varios productos? Sin familia, no hay Abstract Factory.
- Cuando dudes entre Builder y fábrica: la fábrica esconde qué clase; el builder esconde qué pasos. Si el cliente conoce perfectamente la clase (
Pedido) pero sufre montándola, es Builder.
Cómo se combinan entre sí
Los creacionales son piezas de Lego, y en el código real aparecen ensamblados. Las combinaciones canónicas, todas presentes o plausibles en PideYa:
- Abstract Factory implementada con Factory Methods. Cada método
crearX()deFabricaEspanaes un factory method: la fábrica abstracta es, estructuralmente, un paquete de factory methods agrupados por variante. Lo viste en la lección 02-04. - Fábricas expuestas como Singleton (o mejor, inyectadas como instancia única).
FabricaMexicono tiene estado propio: no hay motivo para instanciarla más de una vez. Lo habitual: instancia única creada en la raíz de composición e inyectada; el Singleton clásico queda para el caso sin contenedor. - Builder dentro de una fábrica. Una fábrica cuyo producto es complejo lo monta internamente con su builder:
FabricaEspana.crearFormateadorTicket()puede devolverFormateadorTicket.builder().normativa(ES).moneda(EUR).build(). El cliente ve fábrica; la fábrica usa builder. Cada patrón resuelve su capa. - Fábrica que devuelve el builder.
Pedido.builder(cliente, restaurante)—nuestro punto de entrada de la lección 02-05— es un método estático de fabricación... que fabrica un builder. La combinación es tan natural que ni se nota. - Abstract Factory implementada con Prototype. En vez de una clase de fábrica por mercado, un juego de ejemplares prototípicos por mercado que la fábrica clona. Útil cuando las variantes se definen por configuración (datos) más que por código.
- Registro (de Suppliers o de prototipos) como columna vertebral. El registro de la lección 02-03 y el de plantillas de la lección 02-06 son el mismo esqueleto con relleno distinto: recetas en uno, ejemplares en otro. Ambos convierten "añadir una variante" en "registrar una entrada", sin tocar el núcleo.
La evolución típica: los patrones se ganan
La secuencia que viste en las lecciones no es casual: es la trayectoria natural de un punto de creación durante la vida de un proyecto sano. Conviene tenerla explícita, porque te dice cuándo parar:
flowchart LR
A[new directo] -->|"2ª clase concreta<br/>o duplicación del switch"| B[Simple factory<br/>idiom, no patrón]
B -->|"extensión sin tocar<br/>el núcleo: OCP real"| C[Factory Method<br/>o registro de Suppliers]
C -->|"aparece una 2ª pieza que<br/>debe ser coherente con la 1ª"| D[Abstract Factory]
Cada flecha tiene un peaje de entrada: el síntoma que la justifica. Recorrerla entera "por si acaso" el primer día es la sobreingeniería de la lección 01-06; quedarse en new directo cuando ya hay tres switch duplicados es el error simétrico. PideYa recorrió la secuencia completa solo en pagos e impuestos (donde la internacionalización la exigió); las notificaciones se quedaron en el registro de Suppliers; y decenas de objetos pequeños siguen felizmente en new directo. Las tres paradas son correctas: cada una es la respuesta proporcionada a su nivel de dolor. Builder y Prototype orbitan aparte de esta secuencia: no evolucionan desde la fábrica sino desde el objeto (constructor que engorda → Builder; reconstrucción repetitiva → Prototype). Y el camino inverso también existe: si las variantes de una Abstract Factory se reducen a una y nada apunta a que vuelvan, degradarla a fábrica simple es refactorizar bien, no rendirse (más sobre esto en Refactorización Usando Patrones).
El mapa creacional de PideYa
Cerremos el módulo como empezó: mirando el sistema entero. Esto es lo que quedó instalado, lección a lección, con su justificación en una línea:
| Parte de PideYa | Solución creacional | Por qué esa y no otra |
|---|---|---|
Configuración global (ConfiguracionPideYa) |
Instancia única inyectada desde la raíz de composición (el Singleton clásico quedó como pieza didáctica) | La unicidad es decisión del despliegue; inyectarla mantiene dependencias visibles y tests limpios |
Registro de eventos (RegistroEventos) |
Singleton enum | Recurso técnico, transversal, sin estado de negocio: el caso benigno del patrón |
| Notificadores (push/SMS/email/WhatsApp) | Factory Method en su forma moderna: registro de Supplier<Notificador> |
Un solo producto por canal, extensión frecuente, sin invariante de familia |
| Pagos + impuestos + tickets por mercado | Abstract Factory (FabricaMercado: FabricaEspana, FabricaMexico...) |
Tres piezas con invariante de coherencia por país: la mezcla debe ser imposible por tipos |
Creación de Pedido |
Builder fluido (Pedido.builder(...)) con validación en build() e inmutabilidad |
Muchos opcionales y reglas cruzadas; la clase es conocida, el montaje era el problema |
| Recetas de pedidos frecuentes | Director moderno (RecetasPedido) sobre el builder |
Combinaciones repetidas merecen nombre, no ceremonia GoF completa |
| "Repetir mi último pedido" | Prototype vía constructor de copia (Pedido.clonar()) |
El mejor plano del pedido nuevo es el pedido viejo; la copia profunda de líneas evita corromper el historial |
| Plantillas de carta por tipo de cocina | Prototype con registro de prototipos (RegistroPlantillasCarta) |
Catálogo de ejemplares caros de montar, ampliable con datos sin desplegar código |
Objetos pequeños y estables (Direccion, LineaPedido, Dinero...) |
new directo o records |
Ningún síntoma: cualquier patrón aquí sería ruido |
Fíjate en la última fila: es tan importante como las demás. Un diseño maduro no es el que más patrones tiene, sino el que tiene cada patrón donde su síntoma lo justificó — la vara de medir que fijamos en la lección de la balanza.
Errores Comunes y Consejos
- Elegir por familiaridad, no por problema. "Uso Factory Method porque es el que mejor conozco" produce fábricas donde hacía falta un builder. La guía de decisión existe para forzar las preguntas correctas antes que las respuestas cómodas.
- Confundir la pareja estrella. Factory Method y Abstract Factory seguirán confundiéndose en entrevistas y revisiones; recita el discriminante: un método que crea un producto frente a un objeto que crea una familia coherente.
- Saltarse peldaños de la evolución. Montar la Abstract Factory de mercados cuando PideYa solo operaba en España habría sido flexibilidad especulativa pura. El peaje de cada flecha se paga cuando el síntoma llega, no antes.
- No desmontar patrones que ya no pagan. La evolución también va hacia atrás: mantener una jerarquía de fábricas para una única variante desde hace dos años es deuda estructural con disfraz de diseño.
- Tratar las combinaciones como excepciones. Un builder dentro de una fábrica, o una fábrica que devuelve builders, no es "mezclar patrones": es lo normal. Cada patrón gobierna una capa de la decisión de creación.
- Consejo: en tu próximo proyecto, haz el ejercicio del mapa: una tabla como la de PideYa con cada punto de creación relevante, su solución y su justificación en una línea. Si alguna fila no puede justificarse con un síntoma, tienes un candidato a simplificar.
Ejercicios
Ejercicio 1: diagnóstico exprés
Para cada situación nueva en PideYa, elige el patrón creacional (o la ausencia de patrón) y justifica en una frase con el discriminante adecuado:
- El módulo de informes debe generar un objeto
PanelEstadisticascon 12 widgets configurables, umbrales opcionales y validación de rangos. - Marketing quiere campañas de email cuyo contenido se define visualmente en un panel de administración; cada campaña nueva parte de una existente "que funcionó".
- Al integrar recogida en tienda para supermercados, cada cadena (Mercadona, Carrefour) exige su API de stock, su formato de código de barras y su pasarela de fidelización, siempre a juego.
- Un desarrollador propone hacer singleton el
CarritoActual"para acceder a él desde cualquier pantalla de la app". - Hay que crear el objeto
ExportadorDatosadecuado (CSV, JSON o Parquet) según un parámetro; hoy hay tres formatos y no se prevén familias ni coherencias.
Ejercicio 2: detectar la combinación
Describe qué patrones creacionales colaboran en este fragmento y qué papel cumple cada uno:
public class FabricaMexico implements FabricaMercado {
@Override
public FormateadorTicket crearFormateadorTicket() {
return FormateadorTicket.builder()
.normativa(Normativa.MX)
.moneda(Moneda.MXN)
.leyenda("Este ticket no es un CFDI")
.build();
}
// ...
}Ejercicio 3: la evolución de un punto de creación
El generador de códigos promocionales de PideYa nació como new GeneradorCodigos() en un único servicio. Hoy: (a) marketing pide códigos alfanuméricos para campañas normales y códigos QR para campañas de cartelería; (b) el switch que los distingue ya está copiado en dos servicios. Describe la evolución paso a paso que aplica la secuencia de la sección 4, indicando en qué parada te detendrías hoy y qué síntoma futuro te haría avanzar a la siguiente.
Soluciones
Solución 1:
- Builder: la clase es conocida y el problema es el montaje (opcionales + validación cruzada).
- Prototype con registro: los ejemplares se definen con datos en ejecución y el punto de partida natural es una campaña existente; ninguna solución basada en clases nuevas encaja con un panel de administración.
- Abstract Factory: tres piezas por cadena con invariante de coherencia ("siempre a juego"): familia de manual.
- Ningún patrón, y además rechazo razonado: el carrito es estado de negocio mutable y por usuario; un singleton lo compartiría entre sesiones (bug grave) y ocultaría la dependencia. Corresponde a la sesión/contexto, inyectado.
- Simple factory (o registro de
Suppliersi se prevén más formatos): un producto, sin familias; con tres casos estables, el idiom basta y el patrón completo sería ceremonia.
Solución 2: colaboran Abstract Factory (la clase FabricaMexico, que garantiza la familia coherente del mercado mexicano), Factory Method (el método crearFormateadorTicket() es uno de sus factory methods: decide el producto concreto de esta variante) y Builder (FormateadorTicket.builder()...build() monta el producto complejo paso a paso, con la validación que corresponda en build()). Tres capas de la misma decisión: qué familia → qué producto → cómo se monta.
Solución 3: evolución razonable: (1) el new directo dejó de bastar cuando apareció la segunda clase concreta (alfanumérico/QR) y el switch se duplicó: síntomas claros → extraer una simple factory (FabricaGeneradoresCodigo.crear(tipo)) que deduplica y centraliza. (2) Parada actual: ahí me detendría hoy — dos variantes estables y un solo punto de decisión no justifican más. (3) Síntoma que haría avanzar a Factory Method/registro de Suppliers: nuevas variantes con cadencia real (códigos por partner, por app externa) o la necesidad de que otros módulos registren generadores sin tocar el núcleo. (4) Abstract Factory solo entraría si algún día cada campaña exigiera varias piezas coherentes (generador + validador + render, a juego por tipo de campaña); hoy no existe esa familia, así que anticiparla sería especulación.
Conclusión
El módulo empezó con una pregunta —¿quién decide qué clase concreta se instancia, y dónde?— y ahora tienes cinco respuestas con sus discriminantes: Singleton para la unicidad (mejor, inyección), Factory Method para variar el producto, Abstract Factory para blindar familias, Builder para domar el montaje, Prototype para partir de un ejemplar. Sabes que se combinan por capas, que se llega a ellos por una evolución guiada por síntomas —y que se abandona igual—, y tienes el mapa de PideYa como plantilla del resultado final: cada patrón donde su dolor lo justificó, y new directo donde no había dolor.
Con el nacimiento de los objetos resuelto, la siguiente pregunta es inevitable: una vez creadas las piezas —pasarelas, notificadores, pedidos—, ¿cómo se conectan y organizan entre sí sin que el conjunto se vuelva una maraña? Esa es la segunda familia del catálogo: adaptar interfaces que no encajan, componer estructuras en árbol, envolver objetos con responsabilidades extra, simplificar subsistemas enteros tras una fachada. Nos vemos en la Introducción a los Patrones Estructurales.
Curso de Patrones de Diseño de Software
Módulo 1: Introducción a los Patrones de Diseño
- ¿Qué son los Patrones de Diseño?
- Historia y Origen de los Patrones de Diseño
- Principios de Diseño: SOLID y Otros Fundamentos
- UML Esencial para Entender Patrones
- Clasificación de los Patrones de Diseño
- Ventajas y Desventajas de Usar Patrones de Diseño
Módulo 2: Patrones Creacionales
- Introducción a los Patrones Creacionales
- Singleton
- Factory Method
- Abstract Factory
- Builder
- Prototype
- Comparativa y Elección de Patrones Creacionales
Módulo 3: Patrones Estructurales
- Introducción a los Patrones Estructurales
- Adapter
- Bridge
- Composite
- Decorator
- Facade
- Flyweight
- Proxy
- Comparativa y Elección de Patrones Estructurales
Módulo 4: Patrones de Comportamiento
- Introducción a los Patrones de Comportamiento
- Chain of Responsibility
- Command
- Interpreter
- Iterator
- Mediator
- Memento
- Observer
- State
- Strategy
- Template Method
- Visitor
- Comparativa y Elección de Patrones de Comportamiento
Módulo 5: Aplicación de Patrones de Diseño
- Cómo Seleccionar el Patrón Adecuado
- Ejemplos Prácticos de Uso de Patrones
- Patrones de Diseño en Proyectos Reales
- Refactorización Usando Patrones de Diseño
- Antipatrones: Cuándo los Patrones se Vuelven un Problema
Módulo 6: Patrones de Diseño Avanzados
- Patrones de Diseño en Arquitecturas Modernas
- Patrones de Diseño en Microservicios
- Patrones de Diseño en Sistemas Distribuidos
- Patrones de Concurrencia
- Patrones de Diseño en Desarrollo Ágil
