UML estático: diagrama de clases
"Un diagrama de clases bien hecho vale más que mil líneas de comentarios en el código."
Qué vas a aprender en este capítulo
El diagrama de clases es el diagrama UML más importante. Modela la estructura estática del sistema: qué entidades existen, qué datos tienen, qué operaciones pueden hacer, y cómo se relacionan entre sí. Es el puente entre el análisis (qué necesita el sistema) y el código (cómo se implementa). Antes de escribir una clase en Python, Java o cualquier lenguaje, el diagrama de clases ya debería existir.
Este capítulo asume que conocés programación orientada a objetos del libro de Programación II.
3.1 Clases, atributos y métodos en UML
💡 Intuición
Una clase en UML es la misma clase que conocés de POO — solo que dibujada en un rectángulo dividido en tres secciones:
┌─────────────────────┐
│ NombreClase │ ← nombre (en negrita, centrado, CamelCase)
├─────────────────────┤
│ - atributo1: Tipo │ ← atributos (variables de instancia)
│ # atributo2: Tipo │
├─────────────────────┤
│ + metodo1(): Tipo │ ← métodos (operaciones)
│ + metodo2(p: T): T │
└─────────────────────┘
Si sos Empleado, tenés nombre, salario (atributos) y podés calcularPago(), registrarEntrada() (métodos).
📐 Fundamento
Notación de visibilidad:
| Símbolo | Visibilidad | Significado |
|---|---|---|
+ |
Público | Accesible desde cualquier clase |
- |
Privado | Solo accesible dentro de la misma clase |
# |
Protegido | Accesible en la clase y sus subclases |
~ |
Paquete | Accesible en el mismo paquete/módulo |
Notación de atributos:
visibilidad nombre : Tipo [= valorInicial]
Ejemplos:
- nombre: String# salario: Float = 0.0+ MAX_MESAS: Integer = 10(atributo de clase = subrayado)
Notación de métodos:
visibilidad nombre(param: Tipo) : TipoRetorno
Ejemplos:
+ calcularTotal(): Float+ agregar(item: Platillo, cantidad: Integer): void- validarCredenciales(user: String, pass: String): Boolean
Tipos comunes en UML (independientes del lenguaje):
String, Integer, Float, Boolean, Date, DateTime, List<T>, Set<T>
🛠️ En la práctica
Clase Pedido para La Esquina:
┌──────────────────────────────────────┐
│ Pedido │
├──────────────────────────────────────┤
│ - id: Integer │
│ - fechaHora: DateTime │
│ - estado: String │
│ - mesa: Integer │
├──────────────────────────────────────┤
│ + calcularSubtotal(): Float │
│ + calcularIVA(): Float │
│ + calcularTotal(): Float │
│ + enviarACocina(): void │
│ + cerrar(): void │
└──────────────────────────────────────┘
Reglas prácticas:
- Los atributos casi siempre son privados (
-). - Los métodos casi siempre son públicos (
+). - Los métodos de validación internos son privados.
- No incluyas getters y setters triviales — están implícitos. Solo modelá los métodos con lógica de negocio.
3.2 Relaciones entre clases
💡 Intuición
Las clases raramente viven solas — se relacionan entre sí. Las relaciones en UML tienen matices importantes:
- Asociación: "Un mozo atiende una mesa." Dos clases se conocen y se usan.
- Agregación: "Un departamento tiene empleados, pero si el departamento se disuelve, los empleados siguen existiendo." Una clase contiene a otras, pero son independientes.
- Composición: "Una pupusa tiene ingredientes. Si la pupusa no existe, los ingredientes tampoco tienen sentido en ese contexto." Una clase contiene a otras y son su razón de existir.
- Herencia: "Un administrador ES UN empleado." Una clase extiende otra.
- Dependencia: "Para calcular el IVA, uso una clase
CalculadoraFiscal." Una clase usa otra momentáneamente.
📐 Fundamento
1. Asociación
Línea sólida entre dos clases. Puede tener nombre, dirección y multiplicidad.
Mozo ─────── Pedido
Un mozo puede tener múltiples pedidos. Un pedido pertenece a un mozo.
2. Multiplicidad
Indica cuántos objetos participan en la relación:
| Notación | Significado |
|---|---|
1 |
Exactamente uno |
0..1 |
Cero o uno (opcional) |
* o 0..* |
Cero o más |
1..* |
Uno o más |
n..m |
Entre n y m |
Ejemplo: Un pedido tiene 1..* items. Un item pertenece a 1 pedido.
3. Agregación
Rombo vacío en el lado del "todo". El "parte" puede existir sin el "todo".
Departamento ◇────── Empleado
4. Composición
Rombo lleno en el lado del "todo". Si el "todo" desaparece, la "parte" también.
Pedido ◆────── ItemPedido
Un ItemPedido no existe fuera de un Pedido. Si el pedido se cancela, sus ítems no tienen sentido solos.
5. Herencia (Generalización)
Flecha con triángulo vacío apuntando a la superclase.
Empleado ◁────── Mozo
Empleado ◁────── Administrador
6. Realización / Implementación de interfaz
Flecha punteada con triángulo vacío.
<<interface>> Pagable ◁-------- Pedido
7. Dependencia
Flecha punteada simple. Uso temporal.
Pedido -------> CalculadoraIVA
🛠️ En la práctica
Diagrama de clases completo — La Esquina (texto simplificado):
Empleado
- id: Integer
- nombre: String
- contraseña: String
- rol: String
+ iniciarSesion(u, p): Boolean
◁── Mozo ◁── Administrador
- turno: String + gestionarMenu(): void
+ tomarPedido(): Pedido + verReporteVentas(): Reporte
+ cerrarCuenta(): Float
Pedido Mesa
- id: Integer 1 - numero: Integer
- fechaHora: DateTime ───── - capacidad: Integer
- estado: String - disponible: Boolean
- mozo: Mozo
◆ items: List<ItemPedido> 1..*── ItemPedido
+ calcularTotal(): Float - cantidad: Integer
+ enviarACocina(): void - precioUnitario: Float
◇── Platillo
Platillo - id: Integer
- id: Integer - nombre: String
- nombre: String - precio: Float
- descripcion: String - activo: Boolean
- precio: Float
- activo: Boolean
Reporte
- fecha: Date
- totalVentas: Float
- items: List<LineaReporte>
Diferencia clave composición vs agregación:
Pedido ◆── ItemPedido: Composición. Un ítem de pedido no existe sin su pedido. Si elimino el pedido, elimino los ítems.
Platillo ◇── ItemPedido: Agregación. Un platillo puede existir en el menú aunque no esté en ningún pedido activo. El ItemPedido referencia al Platillo pero no lo posee.
3.3 Cómo identificar clases candidatas
💡 Intuición
Técnica de sustantivos: Lee el enunciado del problema y subrayá todos los sustantivos. Los sustantivos son candidatos a clases. Los verbos son candidatos a métodos.
"El mozo toma el pedido de una mesa, agrega platillos al pedido, y envía el pedido a la cocina. La cocina marca el pedido como listo. El mozo cierra la cuenta y cobra al cliente."
Sustantivos: mozo, pedido, mesa, platillo, cocina, cuenta, cliente. Verbos: tomar, agregar, enviar, marcar, cerrar, cobrar.
Clases candidatas: Mozo, Pedido, Mesa, Platillo, Cliente.
¿Cocina? Probablemente no una clase — es un lugar físico. ¿Cuenta? Podría ser el mismo Pedido con estado "cerrado".
📐 Fundamento
Criterios para incluir o descartar clases:
Incluir si:
- Tiene atributos propios (datos que le pertenecen)
- Tiene comportamiento propio (métodos no triviales)
- Aparece en múltiples casos de uso
Descartar si:
- Solo es un atributo de otra clase (ej:
Direcciónpuede ser un String si no necesita comportamiento) - Es redundante con otra clase
- Es un rol temporal, no una entidad permanente
Clases de dominio vs clases de diseño:
En el análisis se modelan clases de dominio (entidades del negocio). En el diseño se agregan clases de diseño (controladores, repositorios, servicios, DTOs). No mezclar ambos en el mismo diagrama si no es necesario.
3.4 Ejercicios
✏️ Ejercicio 3.1 — Notación UML
Dibujá (o describí en texto) la clase Estudiante con los siguientes datos:
- Atributos privados: id (entero), nombre (texto), carné (texto), gpa (decimal)
- Métodos públicos:
registrar(materia: Materia): Boolean,calcularGPA(): Float - Un atributo de clase (compartido):
universidadNombre: String
Solución
┌────────────────────────────────────────┐
│ Estudiante │
├────────────────────────────────────────┤
│ - id: Integer │
│ - nombre: String │
│ - carné: String │
│ - gpa: Float │
│ __universidadNombre: String__ │ ← subrayado = atributo de clase
├────────────────────────────────────────┤
│ + registrar(materia: Materia): Boolean │
│ + calcularGPA(): Float │
└────────────────────────────────────────┘
✏️ Ejercicio 3.2 — Relaciones
Para un sistema universitario, describí qué tipo de relación existe entre:
a. Universidad y Facultad
b. Facultad y Estudiante
c. Estudiante y Materia (un estudiante cursa muchas materias; una materia tiene muchos estudiantes)
d. Materia y Programa (una materia pertenece a un programa; sin el programa, la materia no tendría contexto curricular)
e. Docente y PersonaActiva (clase abstracta con datos básicos de persona)
Solución
a. Composición — Una facultad no existe fuera de una universidad. Si la universidad se disuelve, la facultad también.
b. Asociación — Los estudiantes pertenecen a una facultad, pero si la facultad cambia de nombre o se reorganiza, los estudiantes siguen existiendo.
c. Asociación muchos-a-muchos (a través de una clase de asociación Inscripción que puede tener atributos como nota, ciclo).
d. Composición — si el programa curricular se elimina, las materias de ese programa pierden sentido dentro del sistema.
e. Herencia (generalización) — Docente es una especialización de PersonaActiva.
✏️ Ejercicio 3.3 — Identificar clases
Lee el siguiente enunciado y:
a. Identificá las clases candidatas. b. Para cada clase, listá al menos 2 atributos relevantes. c. Identificá 2 relaciones con su tipo (asociación, composición, herencia, etc.).
"Un banco tiene clientes. Cada cliente puede tener una o más cuentas (ahorro o corriente). Cada cuenta registra transacciones (depósitos, retiros, transferencias). Las transferencias involucran dos cuentas: origen y destino."
Solución
Clases candidatas:
Banco: nombre, direcciónCliente: id, nombre, DUICuenta(abstracta): numeroCuenta, saldo, fechaAperturaCuentaAhorro: tasaInteresCuentaCorriente: limiteSobregiro
Transaccion(abstracta): id, monto, fechaHoraDepositoRetiroTransferencia: cuentaOrigen, cuentaDestino
Relaciones:
Banco ◆── Cuenta— Composición. Las cuentas existen dentro del banco.Cliente ◇── Cuenta— Agregación. El cliente tiene cuentas; si el cliente se va, las cuentas siguen (historial).Cuenta ──1..*── Transaccion— Composición. Las transacciones no existen sin la cuenta.Cuenta ◁── CuentaAhorro,Cuenta ◁── CuentaCorriente— Herencia.
✏️ Ejercicio 3.4 — Diseño completo
Diseñá el diagrama de clases para un sistema de reserva de salas de conferencia en una empresa:
- Las salas tienen capacidad, nombre y equipamiento (proyector, videoconferencia).
- Los empleados pueden reservar salas por intervalos de tiempo.
- Una reserva tiene fecha, hora de inicio y fin, y puede ser cancelada.
- El sistema debe verificar que no haya choques de horario.
Solución
Sala
- id: Integer
- nombre: String
- capacidad: Integer
- tieneProyector: Boolean
- tieneVideoconferencia: Boolean
+ estaDisponible(inicio: DateTime, fin: DateTime): Boolean
Empleado
- id: Integer
- nombre: String
- email: String
+ reservar(sala: Sala, inicio: DateTime, fin: DateTime): Reserva
Reserva
- id: Integer
- fechaCreacion: DateTime
- inicio: DateTime
- fin: DateTime
- estado: String ("activa", "cancelada")
- empleado: Empleado (asociación)
- sala: Sala (composición: la reserva no existe sin la sala)
+ cancelar(): void
+ hayChoque(otra: Reserva): Boolean
Relaciones:
Empleado 1 ──── 0..* Reserva (un empleado hace muchas reservas)
Sala 1 ──────── 0..* Reserva (composición: reservas pertenecen a la sala)
Nota: hayChoque() verifica si [inicio, fin] de esta reserva se solapa con [inicio, fin] de otra reserva en la misma sala. La verificación: dos intervalos se solapan si inicio1 < fin2 && inicio2 < fin1.
3.5 Para profundizar
- Booch, Rumbaugh & Jacobson, El lenguaje unificado de modelado — el libro canónico de UML.
- Fowler, UML Distilled — versión práctica y concisa de UML (recomendado).
- Larman, UML y Patrones — foco en patrones de análisis y diseño.
- Siguiente: UML dinámico — modelar el comportamiento del sistema en el tiempo.
Definiciones nuevas: diagrama de clases, clase, atributo, método, visibilidad, multiplicidad, asociación, agregación, composición, herencia, generalización, realización, dependencia, clase candidata, clase de dominio.