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ón puede 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

✏️ 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)

✏️ 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."

✏️ 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.

3.5 Para profundizar


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.