Diagramas UML
Cuando un arquitecto diseña un edificio no empieza directamente con los ladrillos — primero elabora planos. Esos planos son un lenguaje visual estandarizado que cualquier arquitecto del mundo puede leer e interpretar sin ambigüedad. En el desarrollo de software ocurre exactamente lo mismo: antes (y durante) la implementación, los desarrolladores necesitan un lenguaje gráfico común para comunicar la estructura y el comportamiento de un sistema. Ese lenguaje es UML.
UML (Unified Modeling Language, Lenguaje Unificado de Modelado) es un estándar internacional mantenido por el Object Management Group (OMG). No es un lenguaje de programación — no se ejecuta. Es un lenguaje de modelado: una notación gráfica para representar sistemas de software de forma abstracta, independientemente del lenguaje de programación en que vayan a implementarse.
UML define catorce tipos de diagramas, organizados en dos grandes familias. Los diagramas estructurales muestran la organización estática del sistema — qué clases existen, qué atributos y métodos tienen, cómo se relacionan entre sí. Los diagramas de comportamiento muestran la dinámica del sistema — cómo fluye la información, en qué orden se ejecutan las operaciones, qué estados puede tener un objeto. En esta unidad estudiaremos los más usados en la práctica profesional.
UML e ingeniería inversa
Diagrama de clases
El diagrama de clases es el diagrama UML más importante y el que más se usa en la práctica. Representa la estructura estática de un sistema orientado a objetos: las clases que lo componen, sus atributos y métodos, y las relaciones que existen entre ellas. Es el plano del sistema — la vista de "qué hay y cómo está organizado".
Un diagrama de clases bien elaborado permite a cualquier desarrollador del equipo entender la arquitectura del sistema sin necesidad de leer miles de líneas de código. También sirve como base de discusión en reuniones de diseño, como documentación de la API interna y como guía para la implementación.
Notación de una clase
En UML, una clase se representa como un rectángulo dividido en tres compartimentos. El superior contiene el nombre de la clase (en negrita, centrado). El central contiene los atributos (campos). El inferior contiene los métodos (operaciones). Delante de cada elemento se indica su visibilidad con un símbolo:+ público,- privado,# protegido,~ de paquete.
Relaciones entre clases
Las relaciones entre clases son la parte más rica y expresiva de los diagramas de clases. Cada tipo de relación tiene una semántica precisa y se representa con un tipo de línea y flecha diferente. Conocerlas y aplicarlas correctamente es la clave para leer e interpretar diagramas de software real.
| Relación | Símbolo UML | Significado | Ejemplo Java |
|---|---|---|---|
| Herencia | ──────▷ (flecha hueca) | Una clase extiende otra. Es una relación 'es un tipo de'. | class Circulo extends Forma |
| Realización | - - - - -▷ (flecha hueca discontinua) | Una clase implementa una interfaz. | class ArrayList implements List |
| Asociación | ───────→ (flecha sólida) | Una clase conoce a otra. Puede tener nombre y multiplicidad. | class Pedido { Cliente cliente; } |
| Agregación | ◇────── (rombo hueco) | 'Tiene un'. Las partes pueden existir sin el todo. | class Departamento { List<Empleado> empleados; } |
| Composición | ◆────── (rombo relleno) | Las partes no pueden existir sin el todo. Relación fuerte. | class Pedido { List<LineaPedido> lineas; } |
| Dependencia | - - - - → (flecha discontinua) | Una clase usa otra puntualmente (como parámetro o variable local). | void procesar(Servicio s) { s.ejecutar(); } |
Multiplicidad
La multiplicidad indica cuántos objetos de cada clase pueden participar en una asociación. Se escribe en ambos extremos de la línea de asociación. Su correcta lectura es fundamental para entender las reglas de negocio del sistema.
| Notación | Significado | Ejemplo de negocio |
|---|---|---|
| 1 | Exactamente uno | Un pedido tiene exactamente un cliente |
| 0..1 | Cero o uno (opcional) | Un empleado puede tener o no un coche de empresa |
| * ó 0..* | Cero o más | Un cliente puede tener ningún pedido o muchos |
| 1..* | Uno o más (al menos uno) | Un pedido tiene al menos una línea de pedido |
| 2..5 | Entre dos y cinco | Un equipo tiene entre 2 y 5 miembros |
| 3 | Exactamente tres | Un triángulo tiene exactamente tres vértices |
Diagrama de clases completo: sistema de pedidos
A continuación se muestra un diagrama de clases de un sistema de gestión de pedidos, con las principales relaciones y multiplicidades. Lee el diagrama de arriba abajo y de izquierda a derecha: un Cliente realiza uno o más Pedidos, cada Pedido tiene una o más LíneaDePedido, y cada LíneaDePedido referencia a un Producto del catálogo.
Del diagrama de clases al código Java
Los IDEs permiten generar código Java a partir de un diagrama de clases y, en sentido inverso, generar el diagrama a partir del código existente (ingeniería inversa). El siguiente ejemplo muestra cómo el diagrama anterior se traduce directamente a código:
1// ── Enum para el estado del pedido ──────────────────────────────────
2public enum EstadoPedido {
3 PENDIENTE, CONFIRMADO, ENVIADO, ENTREGADO, CANCELADO
4}
5
6// ── Producto ─────────────────────────────────────────────────────────
7public class Producto {
8 private String referencia;
9 private String nombre;
10 private double precio;
11 private int stock;
12
13 public boolean hayStock(int cantidadNecesaria) {
14 return stock >= cantidadNecesaria;
15 }
16
17 public void reducirStock(int cantidad) {
18 if (!hayStock(cantidad)) throw new StockInsuficienteException(referencia, cantidad);
19 this.stock -= cantidad;
20 }
21 // getters y setters omitidos por brevedad
22}
23
24// ── LíneaDePedido: composición → no existe sin su Pedido ─────────────
25public class LineaDePedido {
26 private int cantidad;
27 private double precioUnitario;
28 private Producto producto; // asociación → referencia a Producto
29
30 public LineaDePedido(Producto producto, int cantidad) {
31 this.producto = producto;
32 this.cantidad = cantidad;
33 this.precioUnitario = producto.getPrecio();
34 }
35
36 public double getSubtotal() {
37 return cantidad * precioUnitario;
38 }
39}
40
41// ── Pedido ───────────────────────────────────────────────────────────
42public class Pedido {
43 private Long id;
44 private LocalDate fecha;
45 private EstadoPedido estado;
46 // Composición: las líneas no pueden existir sin el pedido
47 private List<LineaDePedido> lineas = new ArrayList<>();
48
49 public double calcularTotal() {
50 return lineas.stream()
51 .mapToDouble(LineaDePedido::getSubtotal)
52 .sum();
53 }
54
55 public void confirmar() {
56 if (lineas.isEmpty()) throw new PedidoVacioException();
57 this.estado = EstadoPedido.CONFIRMADO;
58 }
59
60 public void addLinea(LineaDePedido linea) { lineas.add(linea); }
61}
62
63// ── Cliente ──────────────────────────────────────────────────────────
64public class Cliente {
65 private Long id;
66 private String nombre;
67 private String email;
68 private boolean esVIP;
69 // Asociación: 1 cliente tiene 0..* pedidos
70 private List<Pedido> pedidos = new ArrayList<>();
71
72 public Pedido realizarPedido() {
73 Pedido nuevoPedido = new Pedido();
74 pedidos.add(nuevoPedido);
75 return nuevoPedido;
76 }
77
78 public List<Pedido> getPedidos() {
79 return Collections.unmodifiableList(pedidos);
80 }
81}Diagramas de comportamiento
Mientras que el diagrama de clases muestra la estructura estática del sistema, los diagramas de comportamientomuestran su dinámica: cómo fluye la información, en qué orden se ejecutan las operaciones, qué eventos ocurren y en qué estados puede encontrarse el sistema. UML define varios tipos de diagramas de comportamiento; estudiaremos los cuatro más usados.
Diagrama de casos de uso
El diagrama de casos de uso es el más cercano al usuario. Muestra qué puede hacer el sistema (casos de uso) y quién lo usa (actores). No describe cómo se hace — eso es responsabilidad de otros diagramas. Su propósito es capturar y comunicar los requisitos funcionales del sistema de forma comprensible para personas no técnicas: clientes, usuarios finales, gerentes.
Los elementos básicos son el actor (figura de un hombre de palitos — representa un usuario u otro sistema externo), el caso de uso (óvalo con el nombre de la funcionalidad) y las relaciones: asociación simple entre actor y caso de uso,«include» (el caso A siempre invoca al caso B), y «extend» (el caso B puede extender opcionalmente al caso A).
Casos de uso — Tienda online
Diagrama de secuencia
El diagrama de secuencia muestra cómo los objetos interactúan entre sí a lo largo del tiempo para completar un caso de uso concreto. El tiempo fluye de arriba hacia abajo. Cada objeto tiene una línea de vida (línea vertical discontinua). Las interacciones son mensajes (flechas horizontales entre líneas de vida). Es el diagrama más útil para entender el flujo detallado de una operación compleja.
Diagrama de secuencia — Realizar pedido
Diagrama de actividad
El diagrama de actividad es el equivalente UML de los diagramas de flujo. Muestra el flujo de control de un proceso o algoritmo: qué actividades se realizan, en qué orden, cuándo hay decisiones y bifurcaciones, y cuándo hay actividades paralelas. Es muy útil para modelar procesos de negocio y algoritmos complejos.
Los elementos básicos son: nodo inicial (círculo relleno negro), actividad (rectángulo con esquinas redondeadas), decisión (rombo, con la condición entre corchetes en cada rama), bifurcación/unión (barra horizontal — separa o une flujos paralelos) y nodo final (círculo relleno negro con borde).
Diagrama de actividad — Proceso de compra online
comprando?
productos
checkout
datos de envío
pedido
Diagrama de estados
El diagrama de estados (también llamado diagrama de máquina de estados) modela el ciclo de vida de un objeto concreto: los distintos estados en los que puede encontrarse y las transiciones que lo llevan de un estado a otro. Cada transición se etiqueta con el evento que la desencadena y, opcionalmente, con una condición de guarda (entre corchetes) y una acción (tras la barra).
Diagrama de estados — Pedido
Herramientas para crear diagramas UML
Existen dos grandes enfoques para crear diagramas UML: las herramientas gráficas (en las que arrastras y sueltas elementos con el ratón) y las herramientas textuales (en las que el diagrama se describe en un lenguaje específico y se renderiza automáticamente). Cada enfoque tiene sus ventajas.
| Herramienta | Tipo | Ventajas | Uso recomendado |
|---|---|---|---|
| draw.io / diagrams.net | Gráfica (web, gratuita) | Sencilla, sin instalación, exporta PNG/SVG/XML, integrada en Confluence/Notion | Diagramas de presentación, casos de uso, diagramas de flujo |
| PlantUML | Textual (open source) | Diagrama como código, versión controlada con Git, automatable en CI | Diagramas técnicos que evolucionan con el código |
| Mermaid | Textual (web) | Integrada en GitHub, GitLab, Notion y Markdown. Muy sencilla de usar | Diagramas en README, documentación técnica en repos |
| StarUML | Gráfica (de escritorio) | Potente, soporta todos los tipos UML, ingeniería inversa Java | Proyectos grandes que necesitan todas las características UML |
| IntelliJ IDEA | Integrada en el IDE | Genera diagramas a partir del código Java existente (ingeniería inversa) | Documentar código existente, explorar arquitectura |
| Lucidchart | Gráfica (web, SaaS) | Colaboración en tiempo real, plantillas profesionales | Equipos que trabajan en la nube, diagramas colaborativos |
PlantUML: diagramas como código
PlantUML es una herramienta especialmente interesante porque permite describir los diagramas en texto plano y genera automáticamente la imagen. Al estar en texto, los diagramas se pueden guardar en Git junto al código, se puede ver la evolución del diagrama en el historial, y se pueden generar automáticamente en el pipeline de CI.
1@startuml
2
3' Diagrama de clases — Sistema de pedidos
4skinparam classAttributeIconSize 0
5
6class Cliente {
7 - id: Long
8 - nombre: String
9 - email: String
10 - esVIP: boolean
11 + realizarPedido(): Pedido
12 + getPedidos(): List<Pedido>
13}
14
15class Pedido {
16 - id: Long
17 - fecha: LocalDate
18 - estado: EstadoPedido
19 + calcularTotal(): double
20 + confirmar(): void
21 + cancelar(): void
22}
23
24class LineaDePedido {
25 - cantidad: int
26 - precioUnitario: double
27 + getSubtotal(): double
28}
29
30class Producto {
31 - referencia: String
32 - nombre: String
33 - precio: double
34 - stock: int
35 + hayStock(n: int): boolean
36 + reducirStock(n: int): void
37}
38
39enum EstadoPedido {
40 PENDIENTE
41 CONFIRMADO
42 ENVIADO
43 ENTREGADO
44 CANCELADO
45}
46
47' Relaciones
48Cliente "1" --> "0..*" Pedido : realiza
49Pedido "1" *-- "1..*" LineaDePedido : contiene
50LineaDePedido "*" --> "1" Producto : referencia
51Pedido --> EstadoPedido
52
53@enduml1@startuml
2
3actor Usuario
4participant "ControladorPedidos" as Ctrl
5participant "ServicioPedidos" as Svc
6database "RepositorioPedidos" as Repo
7
8Usuario -> Ctrl : confirmarPedido(pedidoId)
9activate Ctrl
10
11Ctrl -> Svc : procesarPedido(pedidoId)
12activate Svc
13
14Svc -> Repo : buscarPedido(pedidoId)
15activate Repo
16Repo --> Svc : pedido: Pedido
17deactivate Repo
18
19Svc -> Repo : actualizarEstado(CONFIRMADO)
20Repo --> Svc : OK
21
22Svc --> Ctrl : OK
23deactivate Svc
24
25Ctrl --> Usuario : mostrarConfirmacion()
26deactivate Ctrl
27
28@endumlIngeniería inversa en IntelliJ IDEA
Resumen de la unidad
Diagrama de clases
- ✓Representación estática: clases, atributos, métodos y relaciones
- ✓Visibilidad: + público, - privado, # protegido
- ✓Relaciones: herencia, realización, asociación, agregación, composición
- ✓Multiplicidad: 1, 0..1, *, 1..*, n..m
Diagramas de comportamiento
- ✓Casos de uso: actores, funcionalidades, «include» y «extend»
- ✓Secuencia: objetos, líneas de vida, mensajes y retornos
- ✓Actividad: flujo de control, decisiones, paralelismo
- ✓Estados: estados, transiciones, eventos y condiciones de guarda
Herramientas UML
- ✓draw.io / Mermaid: diagramas rápidos y colaborativos
- ✓PlantUML: diagramas como código, versionables con Git
- ✓StarUML: herramienta completa para todos los tipos UML
- ✓IntelliJ IDEA: ingeniería inversa desde código Java
UML en el flujo de desarrollo
- ✓Ingeniería directa: del diagrama al código Java (generación)
- ✓Ingeniería inversa: del código Java al diagrama (documentación)
- ✓Diagramas como documentación viva junto al código fuente
- ✓Integración en pipelines de CI para generar documentación automática