Cuando llevas un tiempo programando con orientación a objetos, empiezas a notar que ciertos problemas se repiten una y otra vez: necesitas que exista una sola instancia de algo, quieres crear objetos sin acoplarte a sus clases concretas, o necesitas que varios componentes reaccionen cuando algo cambia. Los patrones de diseño son precisamente eso: soluciones probadas, con nombre propio, a problemas recurrentes del diseño de software. En esta lección aprenderás qué es exactamente un patrón de diseño, qué elementos lo componen, qué cosas no son un patrón (una confusión muy habitual) y verás un primer problema real que nos servirá de motivación. Además, conocerás PideYa, el proyecto que nos acompañará durante todo el curso.

Contenido

  1. El proyecto del curso: PideYa
  2. Definición de patrón de diseño
  3. Los cuatro elementos esenciales: contexto, problema, solución y consecuencias
  4. Qué NO es un patrón de diseño
  5. Cómo se describe un patrón: la ficha de un patrón
  6. Un primer vistazo motivador: un problema real en PideYa

El proyecto del curso: PideYa

A lo largo de todo el curso trabajaremos sobre un mismo proyecto: PideYa, una plataforma ficticia de pedidos de comida a domicilio. La idea es que no aprendas los patrones con ejemplos abstractos de ClaseA y ClaseB, sino aplicándolos a un dominio realista que iremos haciendo crecer módulo a módulo.

PideYa tiene los ingredientes típicos de una aplicación de este tipo:

  • Restaurantes con su carta de platos, precios y horarios.
  • Clientes que navegan por las cartas, montan un carrito y realizan pedidos.
  • Pagos a través de distintas pasarelas (tarjeta, PayPal, monedero interno...).
  • Repartidores que recogen y entregan los pedidos.
  • Notificaciones a clientes y repartidores (email, SMS, push) según avanza el pedido.

Este dominio es una mina de oro para los patrones de diseño: hay objetos complejos que construir (un pedido con sus líneas, descuentos y dirección de entrega), familias de objetos intercambiables (pasarelas de pago, canales de notificación), estados que cambian (un pedido pasa de recibido a en preparación, en reparto y entregado), y componentes que deben enterarse de lo que hacen otros sin acoplarse entre sí. Cada vez que un ejemplo lo permita, lo plantearemos sobre PideYa.

flowchart LR
    C[Cliente] -->|monta carrito| P[Pedido]
    P -->|paga con| PG[Pasarela de pago]
    P -->|se asigna a| R[Repartidor]
    P -->|genera| N[Notificaciones]
    Rest[Restaurante] -->|prepara| P

No necesitas memorizar nada de PideYa ahora: iremos presentando cada pieza cuando la necesitemos.

Definición de patrón de diseño

Una definición práctica y ampliamente aceptada es la siguiente:

Un patrón de diseño es una solución general, reutilizable y con nombre propio a un problema de diseño que aparece de forma recurrente en un contexto determinado del desarrollo de software orientado a objetos.

Desglosemos esta definición, porque cada palabra importa:

  • Solución general: el patrón no resuelve tu problema concreto, sino la familia de problemas a la que pertenece el tuyo. Por eso siempre hay que adaptarlo.
  • Reutilizable: ha sido aplicado con éxito muchas veces, en muchos proyectos. Un truco que solo funcionó una vez no es un patrón; un patrón está destilado de la experiencia colectiva.
  • Con nombre propio: esto es más importante de lo que parece. Decir "aquí usamos un Observer" comunica en dos palabras una estructura completa de clases, responsabilidades y colaboraciones. Los patrones crean un vocabulario común entre desarrolladores.
  • Problema recurrente: si el problema solo aparece en tu proyecto, la solución puede ser buena, pero no es un patrón.
  • En un contexto determinado: ningún patrón es bueno o malo en abstracto. Un patrón es adecuado en su contexto y contraproducente fuera de él.

Una analogía útil: un patrón de diseño es al software lo que una receta clásica es a la cocina. La receta de la tortilla de patatas te dice los ingredientes, los pasos y los resultados esperados, pero cada cocinero la adapta (¿con cebolla o sin cebolla?). Nadie "copia y pega" una tortilla: la cocina siguiendo el patrón.

Los cuatro elementos esenciales: contexto, problema, solución y consecuencias

Todo patrón de diseño, sea cual sea, se articula sobre cuatro elementos. Si falta alguno, no tienes un patrón completo, tienes solo un fragmento de idea.

Elemento Pregunta que responde Ejemplo informal
Contexto ¿En qué situación aparece el problema? "Una aplicación con varios canales de notificación que pueden crecer en el futuro"
Problema ¿Qué conflicto o fuerza en tensión hay que resolver? "El código que envía notificaciones no debería cambiar cada vez que añadimos un canal nuevo"
Solución ¿Qué estructura de clases/objetos y colaboraciones lo resuelve? "Definir una abstracción común y hacer que el emisor dependa solo de ella"
Consecuencias ¿Qué ganamos y qué pagamos al aplicarla? "Ganamos extensibilidad; pagamos con más clases e indirección"

Merece la pena detenerse en dos de ellos:

Las «fuerzas» del problema

El problema de un patrón nunca es trivial del tipo "quiero sumar dos números". Siempre hay fuerzas en tensión: requisitos que tiran en direcciones opuestas. Por ejemplo: quiero flexibilidad para cambiar el comportamiento en tiempo de ejecución (tira hacia más indirección) frente a quiero un código simple y directo (tira hacia menos clases). El patrón es un equilibrio documentado entre esas fuerzas.

Las consecuencias: no hay patrón gratis

Este punto separa al profesional del aficionado: todo patrón tiene un coste. Más clases, más indirección, más conceptos que el siguiente desarrollador debe entender. Un patrón bien aplicado es aquel cuyo beneficio supera claramente a su coste en ese contexto. Dedicaremos la lección Ventajas y Desventajas de Usar Patrones de Diseño a analizar esto en detalle.

Qué NO es un patrón de diseño

Aquí se concentran los malentendidos más frecuentes. Aclaremos los tres principales:

Un patrón NO es código copiable

No existe "el código del patrón Observer" que puedas pegar en tu proyecto. Un patrón describe una estructura y unas responsabilidades; el código concreto lo escribes tú cada vez, adaptado a tu dominio, tu lenguaje y tus restricciones. Dos implementaciones correctas del mismo patrón pueden no compartir ni una sola línea idéntica. Cuando en este curso veas código Java de un patrón, entiéndelo como una materialización posible, no como la implementación oficial.

Un patrón NO es un framework ni una librería

Un framework (Spring, Hibernate...) es software concreto que ejecutas; un patrón es conocimiento que aplicas. La relación real es la inversa: los frameworks están construidos con patrones. Spring, por ejemplo, usa internamente decenas de ellos, y por eso entender patrones te hace mucho mejor usuario de frameworks: dejas de ver "magia" y empiezas a ver estructuras conocidas.

Patrón de diseño Framework / librería
Naturaleza Conocimiento, descripción Código ejecutable
Cómo se usa Se adapta e implementa cada vez Se importa y se invoca
Dependencia Ninguna: vive en tu cabeza y en tu diseño Tu proyecto depende de él
Nivel Diseño (micro-arquitectura de clases) Implementación

Un patrón NO es un algoritmo

Un algoritmo (ordenación rápida, Dijkstra...) resuelve un problema computacional: dada una entrada, produce una salida en ciertos pasos, y su corrección se puede demostrar. Un patrón resuelve un problema de organización del código: cómo repartir responsabilidades entre clases para que el sistema sea mantenible y extensible. Un algoritmo se mide en eficiencia (tiempo, memoria); un patrón se mide en cualidades de diseño (acoplamiento, cohesión, extensibilidad).

Cómo se describe un patrón: la ficha de un patrón

Los patrones se documentan en catálogos, y cada patrón se presenta con una ficha estandarizada. La estructura clásica (heredada del catálogo del Gang of Four, cuya historia veremos en la siguiente lección) incluye estas secciones:

  1. Nombre: corto y evocador. Es la pieza de vocabulario que compartirás con tu equipo.
  2. Intención (intent): una o dos frases con lo que el patrón consigue.
  3. También conocido como: otros nombres con los que circula.
  4. Motivación: un escenario concreto donde el problema aparece y el patrón lo resuelve.
  5. Aplicabilidad: señales de cuándo usarlo (y, por omisión, cuándo no).
  6. Estructura: diagrama de clases con los participantes. En este curso los dibujaremos con mermaid, y en la lección UML Esencial para Entender Patrones aprenderás a leerlos.
  7. Participantes: cada clase/interfaz del diagrama y su responsabilidad.
  8. Colaboraciones: cómo interactúan los participantes en tiempo de ejecución.
  9. Consecuencias: beneficios y costes.
  10. Implementación: consejos, variantes y trampas al llevarlo a código.
  11. Código de ejemplo: una materialización en un lenguaje concreto.
  12. Usos conocidos: dónde se ha aplicado en sistemas reales.
  13. Patrones relacionados: alternativas y combinaciones habituales.

En este curso usaremos una versión aligerada de esta ficha para cada patrón de los módulos 2, 3 y 4: intención, problema en PideYa, estructura en mermaid, implementación en Java, consecuencias y patrones relacionados. Acostúmbrate a esta estructura: leer patrones "en ficha" es una habilidad en sí misma, porque los catálogos profesionales (libros, wikis de empresa) usan este formato.

Un primer vistazo motivador: un problema real en PideYa

Veamos por qué todo esto importa, con un problema real de PideYa. Atención: aquí solo vamos a sentir el dolor del problema; las soluciones con nombre y apellidos llegarán en sus lecciones correspondientes.

Cuando un pedido cambia de estado (por ejemplo, el restaurante lo marca como "en preparación"), hay que avisar a varias partes: al cliente por notificación push, al repartidor asignado, y al panel de estadísticas internas. Una primera versión ingenua podría ser así:

public class Pedido {

    private String estado;

    public void cambiarEstado(String nuevoEstado) {
        this.estado = nuevoEstado;

        // Avisar al cliente por push
        ServicioPush push = new ServicioPush();
        push.enviar(this.getCliente().getTokenDispositivo(),
                    "Tu pedido está: " + nuevoEstado);

        // Avisar al repartidor por SMS
        ServicioSms sms = new ServicioSms();
        sms.enviar(this.getRepartidor().getTelefono(),
                   "Pedido " + this.getId() + ": " + nuevoEstado);

        // Actualizar estadísticas
        PanelEstadisticas panel = new PanelEstadisticas();
        panel.registrarCambioEstado(this.getId(), nuevoEstado);
    }
}

Expliquemos qué hace este código y por qué, aunque funciona, es un problema esperando a crecer:

  • El método cambiarEstado actualiza el estado y, a continuación, él mismo crea y llama a cada servicio interesado: push, SMS y estadísticas.
  • La clase Pedido, que debería ocuparse de la lógica de negocio de un pedido, conoce los detalles de tres sistemas que no son asunto suyo (cómo se envía un push, qué teléfono tiene el repartidor, que existe un panel de estadísticas).

Ahora imagina la evolución típica del producto:

  • Marketing pide que también se envíe un email en ciertos estados. → Hay que tocar Pedido.
  • Se añade un programa de fidelización que suma puntos al entregar. → Hay que tocar Pedido.
  • En los tests unitarios de Pedido, cada test envía SMS reales (¡y cuestan dinero!) porque los servicios se crean con new dentro del método. → Difícil de testear.
  • El repartidor puede no estar asignado aún → NullPointerException en producción.

El problema de fondo, en términos de patrón, sería:

  • Contexto: un objeto de negocio cuyos cambios interesan a un número variable de terceros.
  • Problema: el objeto no debería conocer a sus interesados ni cambiar cada vez que aparece uno nuevo; las fuerzas en tensión son notificar a todos frente a no acoplarse a nadie.
  • Solución: existe una estructura clásica que resuelve exactamente esto... y tiene nombre. La estudiaremos en el módulo 4 (Observer). También verás que la creación de esos servicios con new toca de lleno a los patrones creacionales del módulo 2.
  • Consecuencias: las analizaremos allí, porque tampoco esa solución es gratis.

Esto es lo que te dará el curso: la capacidad de reconocer estas situaciones, ponerles nombre y aplicar la solución destilada por miles de desarrolladores antes que tú, conociendo su precio.

Errores Comunes y Consejos

  • Creer que aprender patrones es memorizar diagramas. Lo importante es reconocer el problema y sus fuerzas; la estructura se deduce (o se consulta) después. Si memorizas la solución sin el problema, aplicarás patrones donde no tocan.
  • Buscar "el código del patrón" para copiarlo. No existe. Existen ejemplos, como los de este curso, que debes adaptar a tu dominio. Si copias sin adaptar, acabarás con nombres genéricos (Manager, Handler) que no dicen nada de tu negocio.
  • Confundir patrón con framework. Si alguien dice "usamos el patrón Spring", hay una confusión de conceptos. Spring usa patrones; no es un patrón.
  • Querer aplicar un patrón cuanto antes. El impulso de "meter patrones" en cuanto se aprenden es casi universal (y peligroso). Primero domina el problema que resuelve cada uno; la lección 01-06 y el módulo 5 te darán criterios para decidir.
  • Consejo: cuando leas código ajeno (o un framework), practica poniendo nombre a las estructuras que reconozcas. Es el mejor entrenamiento posible y acelera muchísimo la lectura de código.

Ejercicios

Ejercicio 1: contexto, problema, solución y consecuencias

Piensa en la siguiente situación de PideYa y redacta los cuatro elementos de un posible patrón (sin nombrar ningún patrón concreto, aunque lo conozcas): "La aplicación móvil de PideYa debe funcionar con tres pasarelas de pago distintas (tarjeta, PayPal y monedero interno), y el equipo comercial ya está negociando con una cuarta."

Ejercicio 2: ¿patrón o no patrón?

Indica, para cada elemento, si puede considerarse un patrón de diseño y por qué sí o por qué no:

  1. El algoritmo de ordenación quicksort.
  2. La librería java.util.logging.
  3. "Cuando varias clases comparten un comportamiento que varía, extraerlo a una jerarquía propia e inyectarlo, en lugar de heredar."
  4. Un fichero Utilidades.java con métodos estáticos que tu equipo copia de proyecto en proyecto.

Ejercicio 3: detectar las fuerzas

Relee el código de Pedido.cambiarEstado(...) de esta lección. Enumera al menos tres "fuerzas" o requisitos en tensión que hacen que ese diseño sea problemático, expresándolos como pares del tipo "queremos X, pero también queremos Y".

Soluciones

Solución 1 (una redacción posible):

  • Contexto: una aplicación de pedidos que cobra a sus clientes mediante proveedores de pago externos, cuyo número y variedad crece con el negocio.
  • Problema: el código de cobro no debe reescribirse con cada pasarela nueva; cada pasarela tiene su propia API, pero el proceso de pedido debería tratarlas de forma uniforme. Fuerzas: uniformidad para el que cobra frente a diversidad real de las APIs.
  • Solución (esbozo): definir una abstracción común de "pago" de la que dependa el proceso de pedido, y encapsular las particularidades de cada proveedor detrás de esa abstracción, de modo que añadir una pasarela sea añadir una pieza nueva, no modificar las existentes.
  • Consecuencias: se gana extensibilidad y capacidad de probar el cobro sin pasarelas reales; se paga con una capa más de indirección y con el esfuerzo de diseñar una abstracción que encaje con APIs muy distintas.

Solución 2:

  1. No. Es un algoritmo: resuelve un problema computacional con entrada y salida definidas, y se evalúa por eficiencia, no por cualidades de diseño.
  2. No. Es una librería: código concreto que importas y ejecutas. (Internamente aplica patrones, pero ella misma no lo es.)
  3. Sí (o al menos tiene la forma de uno): describe un problema recurrente, una solución general en términos de estructura y responsabilidades, y es adaptable a muchos contextos. De hecho, apunta a un principio que veremos en 01-03: favorecer la composición sobre la herencia.
  4. No. Es código copiable, justo lo que un patrón no es. Puede ser útil, pero no describe problema, contexto ni consecuencias: solo es una implementación concreta que viaja entre proyectos.

Solución 3 (tres ejemplos válidos; hay más):

  • Queremos que todos los interesados se enteren del cambio de estado, pero también queremos que Pedido no conozca a ninguno de ellos.
  • Queremos añadir nuevos avisos (email, fidelización) fácilmente, pero también queremos no modificar Pedido cada vez.
  • Queremos probar Pedido con tests unitarios, pero también queremos que en producción se usen los servicios reales de SMS y push (y con new dentro del método, no hay forma de sustituirlos en los tests).

Conclusión

En esta lección hemos establecido los cimientos del curso: un patrón de diseño es una solución con nombre, probada y adaptable a un problema recurrente, siempre descrita mediante cuatro elementos (contexto, problema, solución y consecuencias) y documentada en fichas estandarizadas. Igual de importante es lo que un patrón no es: ni código copiable, ni framework, ni algoritmo. Y con el problema de las notificaciones de PideYa has visto ya el tipo de situación que los patrones resuelven: código que funciona hoy pero que se degrada con cada nuevo requisito.

Quizá te preguntes de dónde salió esta idea de catalogar soluciones con nombre propio. La respuesta es una historia curiosa que empieza, nada menos, en la arquitectura de edificios. Es el tema de la siguiente lección: Historia y Origen de los Patrones de Diseño.

Curso de Patrones de Diseño de Software

Módulo 1: Introducción a los Patrones de Diseño

Módulo 2: Patrones Creacionales

Módulo 3: Patrones Estructurales

Módulo 4: Patrones de Comportamiento

Módulo 5: Aplicación de Patrones de Diseño

Módulo 6: Patrones de Diseño Avanzados

Módulo 7: Recursos Adicionales y Conclusión

© Copyright 2026. Todos los derechos reservados