El UML es una forma de realizar modelos gráficos de una situación a resolver mediante una solución de software. Así como para construir una casa es necesario un plano, para la construcción de software es necesaria, también, una representación gráfica (y esta necesidad se acrecienta con el tamaño del proyecto).
Un correcto modelo debe representar la estructura y funcionalidad de la pieza de software que se desarrollará, y debe realizarse en forma previa a la codificación, ya que este modelo (junto a otros documentos) será la guía que los programadores seguirán a la hora de codificar. Así, un modelo o diagrama UML será independiente de la implementación. Es decir, no estárá atado a un lenguaje en particular y podría implementarse con cualquier lenguaje de objetos. Es por esto que debe ser lo más genérico posible (por ejemplo, un dato de tipo booleano se indica como “boolean”, sin importar si el lenguaje en el que se va a implementar luego lo llamará “bool”, “Boolean” o de alguna otra forma).
Existen diferentes tipos de diagramas que pueden realizarse pero, en este caso, nos centraremos en los Diagramas de Clases, que ofrecen una visión estática del sistema. Estos diagramas reflejan las clases (con sus atributos y métodos, además de sus modificadores de visibilidad) y las relaciones entre ellas.
En un diagrama de clases se debe representar a cada una de las clases que componen al sistema, pero sólo las modeladas por el diseñador (si se utiliza una clase String, no es necesario graficarla, ya que normalmente esta clase estará incluida en el lenguaje).
Minimalismo
En sistemas complejos y en los cuales intervienen gran número de objetos, los diagramas de clases suelen tornarse ilegibles. Por esto, se opta por representar en el diagrama a la menor cantidad de elementos, los indispensables para su comprensión. Por ejemplo: podrían obviarse los constructores, los métodos getters, los setters, los métodos usados para agregar o quitar elementos de una colección, etc.
También, cuando una clase contiene una colección de objetos (un ejemplo podría ser la clase “Carrera” en una universidad, que tiene un grupo de objetos de tipo “Materia”), este atributo suele no incluirse en la lista de atributos dentro de la caja que representa a la clase, sino que se coloca sobre la línea que representa a la relación entre ambos objetos. Como el tipo de colección o contenedor (ArrayList, Vector, etc.) que se usará para agrupar a los objetos depende del lenguaje, esto no se refleja en el diagrama de clases.
Representación de clases
Para representar a una clase gráficamente se utilizan rectángulos divididos en tres sectores horizontales: el primero para el nombre de la clase; el segundo para los atributos; el tercero para los métodos. Además, debe incluirse el modificador de visibilidad de cada uno de los atributos y métodos (+ para public, - para private, # para protected), el tipo de los atributos y los métodos, y los parámetros que cada método recibe.
La herencia se representa mediante flechas que parten de la subclase y apuntan hacia la superclase.
Si se modela una clase abstracta, esto se indica colocando el nombre de la clase en letra cursiva (o itálica). También suele verse la anotación «abstract» en el nombre de la clase.
Los miembros estáticos (los que poseen el modificador “static”) se indican subrayándolos en el diagrama de clases.
Es posible, también, agregar comentarios en cualquier parte del diseño, uniéndolos mediante una línea punteada al sector del diseño al que afectan:
Cardinalidad
Cuando una clase se relaciona con otra, debe colocarse la cardinalidad a ambos lados de la relación: ésta indica qué tantos objetos de una clase se relacionan con la otra. Por ejemplo, se puede indicar que un (1) objeto contiene una (1) instancia de otro objeto, o que puede contener varias instancias (es decir, una colección de objetos), con un asterisco (*) o la letra n.
Para indicar la cardinalidad pueden utilizarse dos convenciones diferentes: en una, sólo se coloca el máximo a cada lado. Por ejemplo, en una relación entre cuenta bancaria y su titular en la que un titular puede tener una o muchas cuentas bancarias, y una cuenta bancaria puede tener uno o muchos titulares, se colocará el asterisco o la letra n indicando la cardinalidad “muchos”. En la otra forma de escribir las cardinalidades, se colocan tanto el valor mínimo como el máximo, separados por dos puntos. Por ejemplo, si un titular puede tener ninguna o más cuentas bancarias y una cuenta puede tener uno o más titulares se colocará [0..*] para la primera relación y [1..*] para la segunda.
Relaciones entre clases
Si una clase se relaciona de alguna manera con otra clase del modelo, se indica con una línea que une a ambas clases. Si la relación es unidireccional (una clase “conoce”, “posee” o “usa” a otra, pero no se da la situación inversa), se coloca una flecha. Si la relación es bidireccional (cada una de las clases conoce a la otra), la línea no tiene flechas en ningún extremo.
Existen distintos tipos de interacción entre objetos, que en la implementación son utilizados de diversas maneras, según el tipo de situación que se esté modelando: la real diferencia es notoria cuando el sistema es amplio y complejo y posee muchos tipos de relaciones diferentes. En sistemas pequeños puede que un mismo tipo de relación sea practicable en diferentes casos.
Dependencia:
Se trata de una relación débil entre las clases. Una clase usa a otra en algún momento. Si una clase es modificada, esto incide en la clase dependiente.
Hay varios tipos de relaciones que son casos especiales de dependencia con una semántica particular, por ejemplo: la herencia de clases, las asociaciones.
Ejemplos de utilización: una clase tiene métodos que reciben parámetros del tipo de otra clase; un método devuelve un valor del tipo de otra clase; un método usa objetos de otra clase en sus variables internas.
Notación: una línea punteada que parte de la clase que depende de la otra.
Asociación:
Una relación un poco más fuerte que la dependencia, en la que una clase referencia a otra pero ambas tienen existencia independiente. Los objetos de ambas clases podrían asociarse con otras clases, y no únicamente con la clase a la que se encuentran asociados. Por ejemplo: un Empleado usa una Oficina, una Persona toma un Transporte para ir a su casa.
Ejemplos de utilización: una clase tiene atributos del tipo de otra clase; una clase contiene punteros a objetos de otra clase.
Notación: una línea sólida que parte de la clase asociada a la otra.
Agregación:
Se trata de una asociación que representa una relación “parte-todo” en la cual ambos objetos son independientes pero un objeto “es propietario” del otro. Por ejemplo: un Equipo tiene varios Jugadores pero si el equipo se destruye, los Jugadores siguen existiendo. Un Gerente tiene un grupo de Trabajadores a su cargo.
Suele utilizarse la agregación para representar colecciones.
Ejemplos de utilización: una clase tiene atributos del tipo de otra clase; una clase contiene una colección de objetos de otra clase.
Notación: una línea sólida con un rombo vacío en el extremo de origen y una flecha en el extremo final.
Composición:
Se trata de una asociación más fuerte, que representa una relación “parte-todo”, en la cual ambas clases tienen el mismo ciclo de vida (al crearse un objeto de una clase, se crea el objeto asociado a ella y, al destruirse uno, se destruye el otro). Por ejemplo, si una Bicicleta tiene un Asiento y dos Ruedas, si se destruye el objeto Bicicleta, también deben dejar de existir los objetos Asiento y Ruedas ya que no tienen sentido por sí mismos.
Ejemplos de utilización: una clase tiene atributos del tipo de otra clase; una clase contiene una colección de objetos de otra clase.
Notación: una línea sólida con un rombo relleno de color negro en el extremo de origen y una flecha en el extremo final.