Artículos

miércoles, 22 de mayo de 2013

Ejemplos prácticos de Asociación, Agregación, Composición y Dependencia con C# .Net

Motivación

En la actualidad (Mayo 2013) existe mucho material en Internet que habla sobre asociación, composición, agregación y dependencia pero en la mayoría no se pone énfasis en la implementación de los conceptos sino en el análisis conceptual.

La realidad es que la forma de implementar cada tipo de relación es diferente (aunque parezcan mínimas las diferencias) pero es muy importante para un programador conocerlas a la hora de leer un diagrama de clases.

Es importante también aclarar que un diagrama de clases realizado por un analista funcional no es un modelo que se debe implementar así directamente, sino que debe ser analizado y comprendido por el programador para producir el verdadero diagrama de clases a implementar.

Eso es así debido a que durante el análisis y relevamiento se piensa en "describir" y no en solucionar. Así, el diagrama del analista funcional explica el domino del problema, mientras que el diagrama que realiza el programador explica el dominio de la solución. En este último pueden surgir clases que originalmente no estaban pero son necesarias para realizar una buena implementación  (cuestiones de arquitectura, patrones, etc.).

Aplicación práctica y conceptual


Es necesario acostumbrarse al término implementación, el cual se refiere al código de programación concreto de algo.
Por ejemplo, si dicen "esta clase es la implementación de aquella " están diciendo "este es el código fuente específico que aplica lo que dice aquella clase abstracta (o interfaz) ".
Por tal motivo, en adelante, me referiré directamente al término implementación.

Tanto la asociación, agregación, composición y dependencia son formas de representar las relaciones que existen entre clases.

Por ejemplo el siguiente diagrama:



La clase Persona tiene una relación de composición con la clase Domicilio.

Conceptualmente esto significa que los domicilios son una parte inseparable de la persona, por lo que si no existiera una persona entonces el domicilio de la misma debería desaparecer.
Si analizamos más en profundidad encontramos también que, si hubiera que persistir esta relación en una base datos tendríamos una tabla Domicilios cuyo ID sería IdPersona, y una tabla Personas con el mismo ID.
El hecho de que la tabla Domicilios no tenga su propio ID sino el de la otra tabla significa que cada registro de la tabla no tiene el peso propio suficiente, que depende 100% de la existencia del mismo ID en la tabla de Personas.
Si se llegase a borrar un registro de la tabla Personas habría que borrar su correspondiente registro de la tabla Domicilios para mantener la integridad de la información.
Por esta razón también se dice que la relación de composición es una relación fuerte, ya que una instancia arrastra a la otra en caso de eliminación (tanto de objetos en memoria como de registros en base de datos).

Ahora, es válido preguntarse ¿por que razón si es algo inseparable de la persona no lo pongo como un atributo más de la clase persona, por ejemplo de tipo string y no me complico tanto?

Efectivamente la clase Domicilio es un atributo de la clase Persona, y justamente la línea que las conecta es lo que indica la presencia del atributo. Domicilio existe como clase aparte porque en realidad no es un simple string sino un conjunto de atributos, por ejemplo: calle, localidad, numeración, piso, departamento, etc. Todos esos atributos forman parte de una entidad, el Domicilio, y no sería correcto dejarlos sueltos dentro de la clase Persona. Por eso también se persisten en tablas separadas si fuera necesario.

La clase Persona tiene una relación de Agregación con Categoría.

Conceptualmente esto significa que las categorías existen independientemente de la persona que la tenga asignada. En el modelo conceptual esto se corresponde con un enunciado como "una persona puede tener una categoría pero una categoría puede estar presente en muchas personas".
En un modelo de persistencia relacional tendríamos por un lado la tabla Categorías con su IdCategoría y la tabla Persona con un IdCategoría que señale la relación entre ambas tablas.
A diferencia de la composición, en la agregación la clave primaria de la tabla Categorías es independiente de la clave primaria de la persona, lo cual significa que se puede eliminar un registro de Personas sin que ello afecte la integridad de la tabla Categorías.

Ambos tipos de relación muestran que la forma del objeto (persona en este caso) está formado por partes externas.

La clase Persona tiene una relación de Dependencia con Postulación.

Conceptualmente esto significa que la Postulación es un objeto que la Persona utiliza para algún fin, dentro de alguna operación que ella realice (por ejemplo Postularse a un cargo). Pero una Persona no tiene en su interior una Postulación, sino que solo lo utiliza para realizar ciertas operaciones.
Esto es lo más importante y diferenciador respecto de las otras dos relaciones; aquí se pone énfasis en el uso de clases dentro de operaciones, es decir, para que una Persona pueda enviar una postulación depende la clase Postulación , quien es capaz realmente de realizar esa operación.
Al no tratarse de una relación que vincula la forma de los objetos, no existe una forma de persistir esta relación.

La clase Persona tiene una relación de Asociación con Sucursal.

Conceptualmente la asociación en un diagrama de clases implica transitividad y bidirección de clases. Por ejemplo una persona tiene como atributo interno a una Sucursal, pero (y aquí está la diferencia) una Sucursal también tiene un atributo de tipo Persona; la cardinalidad de la asociación indicará si Sucursal tiene una o muchas instancias de Persona, con lo cual en realidad el atributo de Sucursal podría ser una Lista o Vector de Personas.
Otra característica fundamental es que la vida de las instancias de ambas clases no dependen una de la otra.
En un modelo de persistencia relacional podrían suceder dos cosas: si se trata de una cardinalidad de 1 a 1 tendríamos la tabla Personas con su IdPersona más un IDSucusal y la tabla Sucursales con su IdSucursal más un IdPersona.
Cualquier otra cardinalidad requerirá la tabla Personas y Sucursales con sus Id individuales más una tabla que unifique ambas entidades (tabla de relación) con los campos IdPersona e IdSucursal, la cual permite la navegación en dos direcciones sin invadir a ninguna de las tablas reales.

El caso especial se presenta cuando en una asociación es necesario reflejar atributos de información. Por ejemplo, es necesario registrar la fecha de inicio y la fecha de fin de la vinculación entre empleados y sucursales.
Desde el punto de vista de la persistencia no es una complicación ya que la tabla intermedia que vincula a Personas y Sucursales tendrá más campos de información.
Pero desde el punto de vistas de entidades hay un cambio más importante: es necesario crear una nueva entidad. Si un vínculo necesita guardar información deja de ser un vínculo y pasa a ser una entidad nueva, con derecho a tener su propio nombre (no simplemente la unión de los nombres Persona y Sucursal) porque en el paradigma orientado a objetos todo lo que tenga atributos (características) debe ser una entidad. Sin embargo en el modelo de persistencia la tabla intermedia sí suele tener por nombre simplemente la unión de las dos tablas.



Lo que tiene de particular esta entidad es que solo tiene sentido si existen las dos partes, con lo cual en el modelo de persistencia no debe tener clave primaria a menos que esto no sea cierto y la información del vínculo deba persistir más allá de la existencia de las partes que participaron (si así lo dice el negocio...). En este supuesto sí correspondería agregarle una clave primaria a la tabla intermedia.

La implementación de esta entidad intermedia puede resolverse con una clase "TiempoEnSucursal" (disculpen por el nombre, importa ver que tiene nombre propio), la cual no tendrá un identificador propio, pero tendrá una propiedad de tipo Sucursal y otra de tipo Persona (además de la fecha de inicio y fecha de fin propias de la clase).
Por último, estas propiedades de Sucursal y Persona deberían provenir desde afuera de la clase PasoPorSucursal, es decir, por medio de algún constructor o en el "set" de las propiedades.





Para finalizar les dejo la implementación de las clases donde se puede ver en detalle como se aplican los tres tipos de relaciones.



Espero les sea de utilidad este artículo y cualquier inquietud/observación me avisen.

Aquí les dejo todo el fuente más la base de datos.

https://skydrive.live.com/redir?resid=4285FB631C82DC8C!572&authkey=!AM9HFlz_uXOOTUE


Hasta la próxima entrega.


Diego Camacho
dcamacho31@hotmail.com



22 comentarios:

Anónimo dijo...

Amigo muy bueno tu articulo. Estoy comenzando a aprender C# y veía unos videotutoriales donde la persona que explicaba esto no fue claro. Solo una pregunta, Domicilio, Categoría son clases y están declaradas en otra parte... correcto? Si esa clase domicilio contuviera variables, métodos, los puedo accesar desde estas otras clases sin problemas, correcto?

DiegoFox dijo...

Hola.

Mis disculpas por no responder más prontamente pero nunca me llegó el correo de comentario (o entró como spam, lo probaré...)

Si entiendo bien la pregunta, la respuesta es sí: el hecho de que los miembros de Domicilio sean accesibles desde algún lugar lo define el ámbito de alcance que selecciones (private, protected, internal, public).
Lo único que siempre se recomienda es no cambiar el private para las variables internas de una clase (campos) porque sino violarías uno de los principios de la programación orientada a objetos (encapsulamiento). Para ello deberías utilizar una propiedad (publica por ejemplo) que trabaje con ese campo y comparta su valor.

Saludos cordiales!

becavas dijo...

Amigo sería valido reemplazar esta propiedad :

"Public int PersonaId{get;set;}"

por esta propiedad en la clase Domicilio:

"Public Persona persona{get;set;}"

que dices?

Anónimo dijo...

Un domicilio puede existir sin que exista ninguna persona dentro de ella...

Laura_Mejia_A. dijo...

"Anónimo Anónimo dijo...
Un domicilio puede existir sin que exista ninguna persona dentro de ella...

10 de julio de 2014, 14:07"

Sin embargo dentro del contexto, no tiene sentido mantener un domicilio si ya no existe el usuario, ya entiendes?

Por ejemplo ahí explican bien, la categoría en cambio si debe existir porque otros deben hacer uso de ellas.

Anónimo dijo...

Muy buen artículo. Gracias.

matias p dijo...

Es excelente tu artículo, no abunda buena información al respecto...seria muy bueno que publiques mas artículos sobre patrones de diseño con la misma claridad que aquí...saludos!

Anónimo dijo...

becavas dijo...

Amigo sería valido reemplazar esta propiedad :

"Public int PersonaId{get;set;}"

por esta propiedad en la clase Domicilio:

"Public Persona persona{get;set;}"

que dices?

Creo lo mismo que el usuario...estaría en lo cierto es asi???

Unknown dijo...

Muy bueno el artículo! Esperemos que hagas otros!

Felipe Jaramillo Gomez dijo...

Excelente artículo, es claro, conciso y simple.

Muchas gracias.

nenriquez dijo...

Felicidades , tu articulo me resumio las clases del profe de la u, haber si te animas un video sobre los otros diagramas de uml.

Unknown dijo...

La verdad mis felicitaciones por este articulo!!!, te pido por favor si es posible obtener las propiedades de los objetos que participan en la asociacion o composicion con reflection, algun metodo que pueda resolver tal situacion. llevo tiempo intentando resolverlo y no pude. Desde ya muchas gracias!!!!

Unknown dijo...

hola, me gustaria saber como obtener las propiedades de un objeto con reflection que
tiene otro objeto como atributo o una lista de objetos de otra clase como atributo,
por ejemplo: clase persona tiene como atributo a la clase domicilio o podria ser una
lista del tipo domicilio como atributo. Esto es posible mediante reflection???
te agradeceria tu respuesta ya que lllevo tiempo intentando resolver
esta situacion y nada...

Unknown dijo...

Excelente!! Muchas felicitaciones. No había visto que alguien explicara los conceptos de asociación, agregación, composición y dependencia incluyendo la perspectiva de la base de datos. Gran aporte aún en esta fecha.

Anónimo dijo...

Excelente aporte, luego de tantas bùsquedas intensivas en cierto buscador famosìsimo para terminar de hacer mi bendito trabajo pràctico con diagramas UML, debo decir que esta es el primer apunte que encuentro tan bien explicado.Muchìsimas gracias

Héctor dijo...

Y que sucede cuando un domicilio es compartido por varias personas?, ya que en un mismo domicilio pueden vivir varias personas. Si elimina alguna de esas personas (quedando activas o vigentes las demás) se tendría que eliminar el domicilio?

Unknown dijo...

Aplausos!!! Muchos aplausos.
Sabes, lo cual dificultoso es ordenar todos estos conceptos en la cabeza, y poder resumirlos a lo esencial.. Vos lo hiciste!
Soy en parte autodidacta asi que me viene al pelo. Aunque pasó un tiempo, te doy la idea de que agregues aqui o en otro articulo, codigo "cliente" que haga un pequeño uso de éstas clases, sería fenomenal. Gracias por compartir tu experiencia.

Unknown dijo...
Este comentario ha sido eliminado por el autor.
Unknown dijo...

hola primero gracias por tomarte el tiempo para compartir tus conocimientos, tu publicación fue de gran ayuda, pero quisiera darte una observación y si estoy equivocado corrígeme por favor:
la relación de agregación y composición son de sentido semántico, es decir que la observación que te de no afectara tu código, pero en el diagrama que presentas hay errores al momento de asignar el rombo a las clases; la relación entre persona y domicilio es correcta, ya que persona es quien tiene le rombo, debido a que la clase persona es la clase TODO mientras que la clase Domicilio es la clase PARTE; pero no pasa lo mismo en la relación Categoría y Persona, en tu diagrama, la clase Persona es quien tiene el rombo, lo cual es incorrecto, ya que la clase TODO es Categoría y la clase PARTE es Persona, por que una categoría contendrá a muchas personas y una persona estará contenida en una Categoría, por tanto la clase Categoría debería ser quien tenga el rombo.

Espero haberme hecho entender y te agradecería que me respondieras para que así me corrigas(si es que estuviera mal)

Anónimo dijo...

Excelente! clarificador, simple y efectivo. Gracias!

Anónimo dijo...

Hola, que bibliografía recomendás para ampliar?

HERNANDO NIETO dijo...

Muchas gracias. Muy buen articulo.