En un diagrama de clases, las clases aisladas dicen poco. El valor del modelo aparece cuando se muestran las relaciones entre ellas: qué conceptos se conocen, cuáles dependen de otros, cuáles forman parte de un todo y cuáles especializan una idea más general.
UML ofrece varias relaciones para expresar distintos significados. Las más importantes en diagramas de clases son asociación, agregación, composición, dependencia y herencia, también llamada generalización. Usarlas correctamente permite construir modelos más precisos y fáciles de interpretar.
Una relación indica que dos clases no son independientes. Puede significar que una clase conoce a otra, la usa temporalmente, contiene objetos de otra, depende de ella para realizar una operación o representa una variante especializada.
Si se dibujan relaciones sin precisión, el diagrama se vuelve engañoso. Una línea mal elegida puede sugerir una dependencia fuerte donde solo hay un uso ocasional, o puede ocultar que una parte no tiene sentido sin el todo que la contiene.
Las relaciones más habituales permiten expresar distintos grados de vínculo entre clases. La asociación muestra un enlace estructural. La agregación y la composición representan relaciones todo-parte. La dependencia indica uso o necesidad puntual. La herencia expresa especialización entre una clase general y clases más específicas.
La asociación representa un vínculo estructural entre clases. Indica que los objetos de una clase pueden estar conectados con objetos de otra. Por ejemplo, un Cliente puede tener Pedidos, o un Profesional puede atender Turnos.
Una asociación se representa con una línea continua. Puede incluir nombres de roles, multiplicidades y navegabilidad, aunque esos detalles se estudiarán con mayor profundidad en el próximo tema.
Una asociación puede ser bidireccional cuando ambos extremos conocen el vínculo, o unidireccional cuando solo un lado necesita navegar hacia el otro. En UML, la navegabilidad puede indicarse con una flecha.
Por ejemplo, un Pedido puede necesitar conocer al Cliente que lo realizó, pero tal vez el modelo no requiera que Cliente mantenga una colección directa de todos sus pedidos. La decisión depende de lo que se necesita representar, no de una regla fija.
La agregación representa una relación todo-parte débil. El todo agrupa partes, pero las partes pueden existir independientemente. Se dibuja con un rombo blanco en el extremo del todo.
Por ejemplo, una Universidad puede agrupar Departamentos, pero un Departamento podría reorganizarse, trasladarse o existir dentro de otra estructura. La agregación indica pertenencia, aunque no una dependencia fuerte de ciclo de vida.
La composición representa una relación todo-parte fuerte. La parte pertenece al todo de manera más estricta y normalmente no tiene sentido independiente fuera de él. Se dibuja con un rombo negro en el extremo del todo.
Por ejemplo, una Factura puede estar compuesta por DetallesDeFactura. Si se elimina la factura, sus detalles pierden sentido. La composición expresa una dependencia fuerte de pertenencia y ciclo de vida.
La diferencia principal está en la fuerza de la relación. En la agregación, la parte puede existir por separado. En la composición, la parte depende fuertemente del todo. Esta diferencia debe analizarse según el dominio y el propósito del modelo.
| Relación | Significado | Ejemplo |
|---|---|---|
| Agregación | Todo-parte débil; la parte puede existir independientemente. | Equipo y Jugador, Biblioteca y Libro. |
| Composición | Todo-parte fuerte; la parte depende del todo. | Factura y DetalleDeFactura, Pedido y LíneaDePedido. |
La dependencia indica que una clase usa o necesita a otra, pero sin mantener necesariamente una relación estructural permanente. Se representa con una línea discontinua y una flecha hacia la clase de la cual se depende.
Por ejemplo, un ServicioDeTurnos puede depender de un ServicioDeNotificaciones para enviar un aviso después de confirmar una reserva. Si cambia la interfaz del servicio de notificaciones, el servicio de turnos puede verse afectado.
La asociación suele indicar una relación más estable entre objetos. La dependencia suele indicar un uso más puntual o una necesidad para realizar una operación. Elegir entre ambas requiere pensar qué se quiere comunicar.
Si una clase conserva una referencia a otra como parte de su estado, puede corresponder una asociación. Si solo la usa como parámetro, variable local o servicio temporal, puede corresponder una dependencia.
La herencia, en UML llamada generalización, expresa una relación entre una clase general y una clase más específica. Se representa con una línea continua y un triángulo vacío apuntando hacia la clase general.
Por ejemplo, TarjetaDeCrédito y TransferenciaBancaria pueden especializar MedioDePago. Esto significa que comparten una abstracción común, pero cada una puede tener datos o comportamientos propios.
La herencia debe usarse cuando existe una relación clara de tipo "es un". Un Administrador es un Usuario. Un PagoConTarjeta es un MedioDePago. Un Médico es un Profesional. Si la frase no tiene sentido, probablemente no corresponde usar herencia.
No conviene usar herencia solo para reutilizar código o porque dos clases se parecen. La herencia crea una relación fuerte. Si se usa mal, puede volver rígido el diseño.
Una clase abstracta representa una idea general que no se instancia directamente. Sirve como base para clases más específicas. En UML puede indicarse con el nombre en cursiva o con una marca como {abstract}.
Por ejemplo, MedioDePago puede ser abstracta si en el sistema siempre se trabaja con formas concretas como TarjetaDeCrédito, TransferenciaBancaria o BilleteraVirtual.
Una interfaz define operaciones que otras clases deben ofrecer. La relación de realización indica que una clase cumple una interfaz. En UML suele representarse con una línea discontinua y un triángulo vacío apuntando hacia la interfaz.
Por ejemplo, una interfaz Notificador puede definir enviarMensaje(). Luego NotificadorEmail y NotificadorSMS pueden realizar esa interfaz con implementaciones distintas.
Las relaciones deben ser coherentes con el nivel del diagrama. En un modelo conceptual, una asociación puede expresar que Cliente realiza Pedido. En un modelo de diseño, tal vez aparezcan dependencias entre servicios, repositorios e interfaces.
Mezclar relaciones conceptuales con dependencias técnicas puede generar confusión. Antes de dibujar relaciones, conviene aclarar si el diagrama representa el dominio del problema, una solución de diseño o una vista cercana al código.
En un sistema de ventas, Cliente puede estar asociado con Pedido. Pedido puede estar compuesto por LíneaDePedido, porque una línea no tiene sentido sin su pedido. Producto puede asociarse con LíneaDePedido, porque cada línea se refiere a un producto. PagoConTarjeta puede heredar de MedioDePago. ServicioDePedidos puede depender de ServicioDePagos para confirmar el cobro.
Estas relaciones no son intercambiables. Cada una comunica un tipo de vínculo diferente y ayuda a entender la estructura del sistema.
Las relaciones en diagramas de clases permiten representar cómo se conectan las partes del modelo. Elegir correctamente entre asociación, agregación, composición, dependencia y herencia mejora la precisión del diseño y evita interpretaciones incorrectas.
En el próximo tema profundizaremos en multiplicidad, navegabilidad, roles y restricciones en diagramas de clases.