Los patrones de diseño se comunican con diagramas: la sección "Estructura" de cualquier catálogo es un diagrama de clases, y las colaboraciones entre objetos se muestran con diagramas de secuencia. Si no lees estos diagramas con fluidez, cada patrón te costará el doble. La buena noticia: no necesitas dominar UML entero (que tiene 14 tipos de diagramas); para este curso bastan dos —clases y secuencia— y un puñado de relaciones. En esta lección aprenderás exactamente ese subconjunto, verás cómo lo dibujaremos con mermaid en el resto del curso, y practicarás con las clases de PideYa que ya conoces. No es un curso de UML: es tu kit de supervivencia para leer patrones.

Contenido

  1. Qué es UML y qué parte necesitamos
  2. El diagrama de clases: clases, atributos y métodos
  3. Interfaces y clases abstractas
  4. Las seis relaciones que debes reconocer
  5. El diagrama de secuencia básico
  6. Cómo dibujaremos todo esto en el curso: mermaid

Qué es UML y qué parte necesitamos

UML (Unified Modeling Language) es la notación estándar (desde 1997) para dibujar sistemas orientados a objetos. Es enorme, pero los catálogos de patrones usan una fracción mínima:

Diagrama Qué muestra Para qué lo usan los patrones
De clases Estructura estática: clases, interfaces y sus relaciones La sección "Estructura" de cada patrón
De secuencia Interacción dinámica: qué mensajes se envían los objetos y en qué orden La sección "Colaboraciones"

Todo lo demás (casos de uso, actividades, estados, despliegue...) queda fuera de este curso. Regla de oro al leer diagramas de patrones: son esquemáticos, no planos de construcción. Muestran los participantes y relaciones esenciales, omitiendo getters, constructores y detalles accesorios.

El diagrama de clases: clases, atributos y métodos

Una clase se dibuja como una caja con tres compartimentos: nombre, atributos y métodos.

classDiagram
    class Pedido {
        -Long id
        -List~LineaPedido~ lineas
        -EstadoPedido estado
        +calcularTotal() double
        +anadirLinea(LineaPedido linea) void
        +cambiarEstado(EstadoPedido nuevo) void
    }

Cómo se lee cada línea:

  • Visibilidad (el símbolo inicial): + público, - privado, # protegido, ~ de paquete. En los diagramas de patrones casi siempre verás + en métodos y - en atributos: la encapsulación estándar.
  • Atributos: -Long id significa "atributo privado id de tipo Long". En mermaid los genéricos se escriben con ~: List~LineaPedido~ es List<LineaPedido>.
  • Métodos: +calcularTotal() double es un método público sin parámetros que devuelve double. El tipo de retorno va al final (en UML clásico se escribe : double; mermaid lo admite sin los dos puntos).
  • Un miembro subrayado es estático; en mermaid se marca con $ al final: +getInstancia()$ Pedido. Uno en cursiva es abstracto; en mermaid, con *: +cocinar()*.

En Java, esa caja corresponde a:

public class Pedido {
    private Long id;
    private List<LineaPedido> lineas;
    private EstadoPedido estado;

    public double calcularTotal() { /* ... */ return 0; }
    public void anadirLinea(LineaPedido linea) { /* ... */ }
    public void cambiarEstado(EstadoPedido nuevo) { /* ... */ }
}

Interfaces y clases abstractas

Los patrones viven de las abstracciones ("programa contra una interfaz", como vimos en la lección anterior), así que distinguirlas en un diagrama es vital:

  • Una interfaz se marca con el estereotipo <<interface>> sobre el nombre.
  • Una clase abstracta se marca con <<abstract>> (o el nombre en cursiva, en UML clásico).
classDiagram
    class Notificador {
        <<interface>>
        +notificar(Cliente cliente, String mensaje) void
    }
    class MetodoPagoBase {
        <<abstract>>
        -String comercioId
        +procesar(double importe)* ResultadoPago
        +registrarIntento() void
    }

Diferencia práctica al leer un patrón: una interfaz solo declara contrato; una clase abstracta puede además aportar código común a sus hijas (como registrarIntento() arriba) dejando abstracto lo variable (procesar, marcado con *). Muchos patrones se apoyan justo en esa combinación.

Las seis relaciones que debes reconocer

Aquí está el 80% del valor de esta lección. Toda la "gramática" de los diagramas de patrones son estas seis flechas:

Relación Significado Notación UML En mermaid Ejemplo PideYa
Herencia "es un" (extiende una clase) Flecha con triángulo vacío hacia el padre `< --`
Implementación "cumple el contrato de" una interfaz Triángulo vacío con línea discontinua `< ..`
Asociación "conoce a" (referencia duradera) Línea continua (opcionalmente con flecha) --> Pedido conoce a Cliente
Agregación "tiene un" (todo-parte débil: las partes sobreviven al todo) Rombo vacío en el lado del todo o-- Repartidor agrupa Vehiculo (el vehículo existe sin él)
Composición "está compuesto de" (todo-parte fuerte: las partes mueren con el todo) Rombo relleno en el lado del todo *-- Pedido se compone de LineaPedido
Dependencia "usa temporalmente" (parámetro, variable local, creación) Flecha discontinua ..> GeneradorFacturas usa Pedido como parámetro

Y así se ven todas juntas sobre clases de PideYa:

classDiagram
    class Notificador {
        <<interface>>
        +notificar(Cliente c, String msg) void
    }
    class MetodoPagoBase {
        <<abstract>>
        +procesar(double importe)* ResultadoPago
    }

    MetodoPagoBase <|-- PagoTarjeta : herencia
    Notificador <|.. NotificadorSms : implementación
    Pedido --> Cliente : asociación
    Repartidor o-- Vehiculo : agregación
    Pedido *-- LineaPedido : composición
    GeneradorFacturas ..> Pedido : dependencia

Claves para no confundirlas:

  • El triángulo siempre apunta a la abstracción (padre o interfaz). Línea continua = herencia de clase; discontinua = implementación de interfaz.
  • El rombo va en el lado del "todo", no de la parte. Para distinguir agregación de composición pregúntate: si borro el todo, ¿tiene sentido que la parte siga existiendo? Si el pedido se cancela y borra, sus líneas no significan nada sueltas → composición (rombo relleno). Si el repartidor se da de baja, la moto sigue existiendo en la flota → agregación (rombo vacío).
  • Asociación frente a dependencia: asociación es un atributo (referencia que se conserva); dependencia es un uso puntual (parámetro o variable local). En Java: private Cliente cliente; es asociación; generarPdf(Pedido p) es dependencia.
  • Las asociaciones pueden llevar multiplicidad: "1" --> "0..*" se lee "un pedido tiene de cero a muchas líneas". En mermaid: Pedido "1" *-- "1..*" LineaPedido.

La correspondencia en Java de las tres relaciones "de tener":

public class Pedido {
    private Cliente cliente;                                // Asociación
    private final List<LineaPedido> lineas = new ArrayList<>(); // Composición:
    // las líneas se crean dentro del pedido y nadie más las referencia

    public void anadirLinea(Plato plato, int cantidad) {
        lineas.add(new LineaPedido(plato, cantidad));       // nacen y mueren con él
    }
}

public class Repartidor {
    private Vehiculo vehiculo;                              // Agregación:
    public void asignarVehiculo(Vehiculo v) { this.vehiculo = v; } // viene de fuera
}

Matiz honesto: en Java la diferencia agregación/composición no la impone el lenguaje (ambas son referencias); es una intención de diseño sobre ciclo de vida y propiedad. En los diagramas de patrones no suele ser crítica: si dudas, léela como "tiene un".

El diagrama de secuencia básico

El diagrama de clases dice quién es quién; el de secuencia dice qué pasa cuando el sistema se ejecuta: qué objetos participan, qué mensajes (llamadas a métodos) se envían y en qué orden temporal (el tiempo fluye hacia abajo).

Escenario PideYa: un cliente confirma su carrito y el sistema cobra y notifica.

sequenceDiagram
    participant C as Cliente
    participant SP as ServicioPedidos
    participant MP as MetodoPago
    participant N as Notificador

    C->>SP: confirmar(carrito)
    activate SP
    SP->>SP: crearPedido(carrito)
    SP->>MP: procesar(importe)
    activate MP
    MP-->>SP: ResultadoPago
    deactivate MP
    SP->>N: notificar(cliente, "Pedido confirmado")
    SP-->>C: pedidoConfirmado
    deactivate SP

Elementos que debes reconocer:

  • Participantes (arriba): los objetos involucrados. De cada uno cuelga su línea de vida vertical.
  • Mensaje síncrono (->>, flecha continua): una llamada a método; el emisor espera la respuesta.
  • Respuesta (-->>, flecha discontinua): el valor devuelto.
  • Barra de activación (el rectángulo sobre la línea de vida, activate/deactivate): el intervalo en que ese objeto está ejecutando algo.
  • Automensaje (SP->>SP): el objeto llama a un método propio.

En los patrones, el diagrama de secuencia responde a la pregunta que el de clases no puede responder: "vale, estas son las clases... pero ¿quién llama a quién y cuándo?". Verás que en varios patrones la estructura de clases es casi idéntica y lo que los distingue es la secuencia; por eso conviene leer ambos siempre.

Cómo dibujaremos todo esto en el curso: mermaid

En este curso todos los diagramas están escritos en mermaid, una notación de texto que se renderiza como diagrama. Ventaja para ti: puedes copiar cualquier diagrama del curso, pegarlo en mermaid.live y modificarlo para experimentar. La chuleta completa de lo que usaremos:

classDiagram                          %% inicia un diagrama de clases
    class MiClase {
        <<interface>>                 %% o <<abstract>>
        -tipo atributoPrivado
        +metodo(Tipo param) TipoRetorno
        +metodoAbstracto()* Tipo      %% * = abstracto
        +metodoEstatico()$ Tipo       %% $ = estático
    }
    Padre <|-- Hija                   %% herencia
    Interfaz <|.. Implementacion      %% implementación
    A --> B : conoce                  %% asociación (con etiqueta opcional)
    Todo o-- Parte                    %% agregación
    Todo *-- Parte                    %% composición
    Usuario ..> Usado                 %% dependencia
    A "1" --> "0..*" B                %% multiplicidad

sequenceDiagram                       %% inicia un diagrama de secuencia
    participant A as NombreLegible
    A->>B: llamada(args)              %% mensaje síncrono
    B-->>A: respuesta                 %% retorno
    activate B                        %% empieza activación
    deactivate B                      %% termina activación

Con esta chuleta puedes leer el 100% de los diagramas de los módulos 2 a 6. Cuando en el módulo 2 veas la estructura de un patrón, ya no estarás descifrando flechas: estarás leyendo diseño.

Errores Comunes y Consejos

  • Confundir el sentido del triángulo de herencia. El triángulo toca a la abstracción (padre/interfaz). En mermaid, MetodoPagoBase <|-- PagoTarjeta se lee "PagoTarjeta hereda de MetodoPagoBase". Si lo lees al revés, entenderás todos los patrones del revés.
  • Confundir el lado del rombo. El rombo va pegado al todo (el contenedor), no a la parte. Pedido *-- LineaPedido: el rombo está en Pedido.
  • Intentar que el diagrama lo diga todo. Un diagrama de patrón es un esquema: si no aparecen los getters o el constructor, no es que no existan; es que no importan para entender el patrón. No los añadas al dibujar los tuyos.
  • Ignorar la diferencia entre línea continua y discontinua. Continua = relación fuerte y estructural (herencia, asociación); discontinua = relación más débil o de contrato (implementación, dependencia). Este matiz cambia el significado del diagrama.
  • Leer solo el diagrama de clases y saltarse el de secuencia. Varios patrones comparten estructura estática casi idéntica; la diferencia está en la dinámica. Acostúmbrate desde ya a leer los dos.
  • Consejo: practica el camino inverso. Toma una clase pequeña de un proyecto tuyo y dibújala en mermaid con sus relaciones reales. Dibujar es lo que fija la notación; leer solo, no.

Ejercicios

Ejercicio 1: de diagrama a Java

Traduce este diagrama a esqueletos de clases Java (sin implementar los cuerpos):

classDiagram
    class Promocion {
        <<interface>>
        +calcularDescuento(Pedido pedido) double
    }
    Promocion <|.. PromocionPrimeraCompra
    CalculadoraDescuentos ..> Promocion
    CalculadoraDescuentos ..> Pedido
    Pedido "1" *-- "1..*" LineaPedido

Ejercicio 2: de Java a diagrama

Dibuja en mermaid (classDiagram) las clases y relaciones de este código, eligiendo bien entre asociación, agregación, composición y dependencia:

public class Restaurante {
    private final Carta carta = new Carta();       // creada y poseída por el restaurante
    private List<Repartidor> repartidoresFavoritos; // se asignan desde la flota común

    public Factura facturarMes(GeneradorFacturas generador) {
        return generador.generarMensual(this);
    }
}

Ejercicio 3: leer una secuencia

Observa el diagrama de secuencia de la sección 5 y responde: (a) ¿qué objeto orquesta el proceso?, (b) ¿en qué momento está activo MetodoPago y qué devuelve?, (c) ¿el mensaje a Notificador espera respuesta según el diagrama? ¿Qué mensaje es un automensaje?

Soluciones

Solución 1:

public interface Promocion {
    double calcularDescuento(Pedido pedido);
}

public class PromocionPrimeraCompra implements Promocion {   // <|.. implementación
    public double calcularDescuento(Pedido pedido) { return 0; }
}

public class CalculadoraDescuentos {
    // ..> dependencia: usa Promocion y Pedido como parámetros, no como atributos
    public double calcular(Pedido pedido, Promocion promocion) { return 0; }
}

public class Pedido {
    // *-- composición 1 a 1..*: el pedido posee sus líneas (al menos una)
    private final List<LineaPedido> lineas = new ArrayList<>();
}

public class LineaPedido { }

Solución 2:

classDiagram
    Restaurante "1" *-- "1" Carta : composición
    Restaurante o-- Repartidor : agregación
    Restaurante ..> GeneradorFacturas : dependencia
    Restaurante ..> Factura : dependencia

Razonamiento: la Carta la crea y posee el restaurante (muere con él) → composición. Los repartidores existen fuera del restaurante y solo se le asocian como favoritos → agregación (una asociación simple --> también sería defendible; lo importante es descartar la composición). GeneradorFacturas y Factura solo aparecen como parámetro y valor de retorno de un método → dependencias.

Solución 3: (a) ServicioPedidos: recibe la petición del cliente y llama a todos los demás. (b) MetodoPago está activo solo entre la llamada procesar(importe) y su respuesta; devuelve un ResultadoPago. (c) Tal como está dibujado, a notificar(...) no se le dibuja flecha de retorno: el diagrama no muestra respuesta (se lee como llamada cuyo resultado no interesa en este escenario). El automensaje es SP->>SP: crearPedido(carrito): ServicioPedidos invocando un método propio.

Conclusión

Ya tienes el kit de lectura de diagramas que usarás en todo el curso: la caja de clase con visibilidades, los estereotipos <<interface>> y <<abstract>>, las seis relaciones (herencia, implementación, asociación, agregación, composición y dependencia, con sus flechas y rombos bien orientados) y el diagrama de secuencia para la dinámica. Además conoces la sintaxis mermaid exacta con la que están escritos todos los diagramas del curso, lista para copiar y experimentar.

Con las herramientas de lectura en la mano, toca desplegar el mapa: ¿cuántos patrones hay, cómo se organizan y qué hace cada familia? Ese mapa —que es también el itinerario de los módulos 2, 3 y 4— es la próxima lección: Clasificación 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