Las relaciones son importantes

Obteniendo Diagramas de Clases

Dicen por ahí que “lo prometido es deuda”, así que en esta ocasión hablaremos del modelado de las relaciones entre las clases de diseño. Este compromiso lo adquirimos, Sergio Orozco y un servidor, en el artículo publicado en la edición de Marzo-Abril de 2006 de esta revista… también dicen por ahí que “más vale tarde que nunca”.En el artículo antes mencionado expusimos las técnicas aplicables para obtener un diagrama de clases a partir de uno de secuencia, las cuales podemos resumir en el cumplimiento de tres reglas:
1. Cualquier línea de vida (objeto) que aparezca como participante en el diagrama de secuencia, debe ser instancia de una clase.
2. Cualquier mensaje recibido por una línea de vida (objeto) debe estar soportado por una operación definida en la clase de la cual es instancia.
3. Cualquier intercambio de mensajes entre dos líneas de vida (sobre todo cuando éstas son instancias de clases distintas) debe verse reflejado mediante algún tipo de relación (asociación binaria, agregación de tipo shared, agregación de tipo composite o dependencia) en el diagrama de clases.

En este artículo nos enfocaremos en la tercera regla, porque es la que quedó, digamos “oscura”, en el artículo anterior. Las figuras 1 y 2 nos ayudarán a regresar al contexto del ejemplo que tomaremos como base para el resto de la disertación.

Como podemos observar en el diagrama de secuencia (Figura 1), la instancia de la clase TPV le envía tres mensajes a la instancia de la clase Venta, y esta última a su vez, le envía un mensaje a la instancia de la clase RenglonVenta, así

Figura 1. Diagrama de secuencia para registrar una venta.

que, si aplicamos la tercera regla, debe existir algún tipo de relación entre TPV y Venta, así como entre Venta y RenglonVenta (Figura 2).


Figura 2. Diagrama de clases derivado del diagrama de secuencia.

El asunto es responder a la pregunta ¿por qué entre TPV y Venta se modeló una dependencia, mientras que entre Venta y RenglonVenta una composición?

La respuesta o respuestas cortas son: porque la relación entre TPV y Venta no es estructural, mientras que la relación entre Venta y RenglonVenta sí lo es. O también podríamos decir que: porque entre TPV y Venta se forma un enlace transitorio, mientras que entre Venta y RenglonVenta se establece uno permanente. La segunda forma de explicarlo es la que prefiero, y estoy convencido que es la que resulta más clara, por tanto, es la que usaré para la respuesta larga.

La diferencia entre el modelado de una dependencia o de una asociación (binaria, agregación shared o agregación composite) radica en la transitoriedad o permanencia del enlace entre los objetos que participan en la interacción, es decir, qué tanto dura el enlace.

Enlace transitorio
Si el enlace entre los objetos tiene el alcance de una ocurrencia de ejecución (rectángulo delgado que se modela sobre la línea de vida, y que en código lo veríamos como el cuerpo de la operación) entonces en el diagrama de clases lo modelaríamos como dependencia. Cuando en el código vemos que desde el cuerpo de una operación (método) podemos acceder a los servicios de un objeto, ya sea porque recibimos una referencia hacia el mismo mediante parámetro o porque creamos esa referencia localmente al instanciarlo, o porque dicha referencia es consecuencia de que el objeto sea de acceso global (como en el patrón del Singleton) entonces modelamos una dependencia. En el caso de nuestro ejemplo y apoyándonos en el siguiente fragmento de código, podemos observar que en el método “registrarVenta” se crea una referencia hacia una instancia de la clase Venta llamada “nuevaVenta”, pero dicha referencia se pierde al concluir el método.

public class TPV {
public void registrarVenta(String ventaXML){

Venta nuevaVenta = new Venta(ventaXML);
nuevaVenta.agregarRenglonVenta
(renglonesVentaXML);
nuevaVenta.registrarPago(monto);

}
}
Enlace permanente
Si el enlace entre los objetos trasciende ocurrencias de ejecución, entonces significa un enlace permanente y se modelará una asociación (binaria, agregación shared o agregación composite) en el diagrama de clases. Para conseguir que un enlace trascienda ocurrencias de ejecución, es necesario alojar la referencia hacia el objeto en un atributo de la clase. En nuestro ejemplo podemos observar que los renglones venta son alojados en la colección “renglonesVenta”, la cual es un atributo de la clase Venta, esto hace posible que las referencias hacia los objetos del tipo ReglonVenta sobrevivan a la conclusión del método “agregarRenglonVenta”.

public class Venta {
List< RenglonVenta > renglonesVenta =
new ArrayList();
public void agregarRenglonVenta
(String renglonesVentaXML){

renglonesVenta.add
(new RenglonVenta(cantidad, idProducto));

}
}

En nuestro ejemplo, la relación entre Venta y RenglonVenta resulta ser una agregación del tipo composite, lo que le indica al programador que el manejo de la colección debe ser por valor, y que la relación debe ser simétrica. Pero bien pudimos haber modelado una agregación del tipo shared si nuestra decisión de diseño hubiera sido que el manejo de la colección fuera por referencia. Una relación de asociación binaria se modelaría en el caso de que el enlace entre los objetos estuviera limitado a una sola instancia de sendas clases.

Las relaciones entre las clases
manifiestan decisiones de diseño
Finalmente, una idea importante es que las relaciones de los distintos tipos dependen de las decisiones de diseño que tomen los que plantean la solución al problema, y cuando hablamos de problemas para los que aplican soluciones de ingeniería, son problemas de solución abierta, es decir, hay muchas posibles soluciones, unas mejores que otras. Algunos criterios podrían ser los siguientes:
• Si un objeto requiere de un enlace hacia otro objeto una y otra vez, es decir, que parece permanecer relacionado al otro incluso a través de la ejecución de una o más operaciones, entonces probablemente una relación de asociación sea lo más conveniente.
• Si un objeto se enlaza con otro para consumir sus servicios, y después lo deshecha, entonces podríamos preferir una dependencia.
• Si en tiempo de ejecución varios objetos necesitan y comparten de un tercero, una y otra vez, quizá podíamos decidir compartir al tercero pasándolo como parámetro. También podríamos decidir o necesitar que el objeto compartido fuera único y global en el proceso (patrón del Singleton), en cualquiera de estos dos casos, modelaríamos una dependencia.
• Si a fin de mejorar el desempeño y escalabilidad del sistema, descubrimos que nos sale más barato mantener un objeto en memoria que creando y destruyendolo podríamos decidir alojarlo en una variable de clase (atributo) y esto nos llevaría a escoger una asociación.


Conclusión
En términos simples:
• Si el enlace entre dos instancias de clases distintas que colaboran para alcanzar un objetivo es transitorio, entonces se modela una dependencia.
• Si el enlace entre dos instancias de clases distintas que colaboran para alcanzar un objetivo es permanente (sobrevive a ocurrencias de ejecución), entonces se modela una asociación (binaria, agregación de tipo shared, agregación de tipo composite).
La temporalidad de los enlaces, y por tanto la naturaleza de las relaciones entre las clases, obedece a las decisiones de diseño que se tomen al plantear la solución al problema.

Acerca del Autor

Charlie Macías es arquitecto en jefe e instructor senior en Milestone Consulting. Primer empresa mexicana miembro de la OMG, especializada en la capacitación práctica y consultoría en modelado de sistemas y negocios con UML, BPMN y SysML. Puedes contactarnos en info@milestone.com.mx www.milestone.com.mx