java - example - Agregación versus composición
table swing java (12)
Me ha costado entender la diferencia entre composición y agregación en UML. ¿Puede alguien por favor ofrecerme una buena comparación y contraste entre ellos? También me encantaría aprender a reconocer la diferencia entre ellos en el código y / o ver un pequeño ejemplo de software / código.
Editar: Parte de la razón por la que pregunto es debido a una actividad de documentación inversa que estamos haciendo en el trabajo. Hemos escrito el código, pero tenemos que regresar y crear diagramas de clase para el código. Simplemente nos gustaría capturar las asociaciones correctamente.
Como una regla de oro:
class Person {
private Heart heart;
private List<Hand> hands;
}
class City {
private List<Tree> trees;
private List<Car> cars
}
En la composición (Persona, Corazón, Mano), los "sub objetos" (Corazón, Mano) serán destruidos tan pronto como la Persona sea destruida.
En agregación (Ciudad, Árbol, Automóvil) los "sub objetos" (Árbol, Automóvil) NO serán destruidos cuando la Ciudad sea destruida.
La conclusión es que la composición acentúa la existencia mutua y, en conjunto, esta propiedad NO es necesaria.
Considera partes del cuerpo humano como riñón, hígado, cerebro. Si tratamos de mapear el concepto de composición y agregación aquí, sería como:
Antes del advenimiento del trasplante de partes del cuerpo como el de los riñones y el hígado, estas dos partes del cuerpo estaban en composición con el cuerpo humano y no pueden existir aislamiento con el cuerpo humano.
Pero después del advenimiento del trasplante de parte del cuerpo, pueden trasplantarse a otro cuerpo humano, por lo que estas partes se agregan al cuerpo humano ya que su existencia aislada con el cuerpo humano es posible ahora.
El ejemplo que aprendí fue con los dedos en la mano. Tu mano está compuesta de dedos. Los posee. Si la mano muere, los dedos mueren. No puedes "agregar" dedos. No puedes simplemente agarrar dedos extra y unirlos y separarlos de tu mano a voluntad.
El valor aquí, desde el punto de vista del diseño, a menudo se relaciona con la duración del objeto, como dijo otro cartel. Digamos que tienes un cliente y ellos tienen una cuenta. Esa Cuenta es un objeto "compuesto" del cliente (al menos, en la mayoría de los contextos que se me ocurre). Si elimina al Cliente, la Cuenta no tiene ningún valor por sí misma, por lo que también se eliminará. Lo contrario a menudo es cierto en la creación de objetos. Debido a que una Cuenta solo tiene significado en el contexto de un Cliente, la creación de la Cuenta ocurriría como parte de la creación del Cliente (o, si lo hace de forma perezosa, sería parte de alguna transacción del Cliente).
En el diseño, es útil pensar qué objetos poseen (componer) otros objetos frente a los que simplemente hacen referencia (agregan) otros objetos. Puede ayudar a determinar dónde recae la responsabilidad de la creación / limpieza / actualizaciones de objetos.
En cuanto al código, a menudo es difícil de decir. La mayoría de todo en el código es una referencia de objeto, por lo que puede no ser obvio si el objeto al que se hace referencia es compuesto (propiedad) o agregado.
El ejemplo que me gusta: Composición: el agua es parte de un estanque. (Estanque es una composición de agua.) Agregación: Estanque tiene patos y peces (Estanque agrega patos y peces)
Como puede ver, he marcado en negrita "parte de" y "tengo", ya que estas 2 frases pueden indicar qué clase de conexión existe entre las clases.
Pero, como señalaron otros, muchas veces, si la conexión es una composición o una agregación, depende de la aplicación.
En términos de código, la composición generalmente sugiere que el objeto contenedor es responsable de crear instancias del componente *, y el objeto contenedor contiene las únicas referencias de larga vida al mismo. Por lo tanto, si el objeto principal se redirige y se recoge basura, también lo hará el niño.
entonces este código ...
Class Order
private Collection<LineItem> items;
...
void addOrderLine(Item sku, int quantity){
items.add(new LineItem(sku, quantity));
}
}
sugiere que LineItem es un componente de Order - LineItems no tienen existencia fuera de su orden de contenido. Pero los objetos Item no están construidos en el orden: se transfieren según sea necesario y continúan existiendo, incluso si el taller no tiene pedidos. entonces están asociados, en lugar de componentes.
* nb el contenedor es responsable de la instancia del componente, pero puede que en realidad no llame al nuevo ... (), ya que es Java, ¡generalmente hay una fábrica o dos para pasar primero!
Es muy difícil hacer una diferencia entre la relación agregada y la relación compuesta, pero voy a tomar algunos ejemplos. Tenemos una casa y habitaciones, aquí tenemos una relación compuesta, la sala es parte de la casa, y la vida de la habitación comenzó con la vida de la casa y terminará cuando termine la vida de la casa, la habitación sea parte de la casa, hablamos de composición, como país y capital, libro y páginas. Para el ejemplo de relación agregada, tome equipo y jugadores, el jugador puede existir sin equipo, y el equipo es un grupo de jugadores, y la vida del jugador puede comenzar antes de la vida del equipo, si hablamos de programación, podemos crear jugadores y después crearemos un equipo, pero para la composición no, creamos habitaciones dentro de la casa. Composición ----> compuesto | componiendo. Agregación -------> grupo | elemento
Es sorprendente cuánta confusión existe sobre la distinción entre la parte total , la agregación de conceptos de asociación y la composición . El problema principal es el malentendido generalizado (incluso entre los desarrolladores de software expertos y entre los autores de UML) de que el concepto de composición implica una dependencia del ciclo de vida entre el todo y sus partes, de modo que las partes no pueden existir sin el todo. Pero este punto de vista ignora el hecho de que también hay casos de asociaciones de parte-todo con partes no compartibles donde las partes pueden separarse y sobrevivir a la destrucción del todo.
En el documento de especificación UML, la definición del término "composición" siempre ha implicado partes no compartibles, pero no ha quedado claro cuál es la característica definitoria de "composición", y qué es meramente una característica opcional. Incluso en la nueva versión (a partir de 2015), UML 2.5, después de un intento de mejorar la definición del término "composición", sigue siendo ambigua y no proporciona ninguna guía sobre cómo modelar las asociaciones de parte a conjunto con las no partes compartibles donde las partes pueden separarse y sobrevivir a la destrucción del todo, en oposición al caso en que las partes no se pueden separar y se destruyen junto con el todo. Ellos dicen
Si se elimina un objeto compuesto, todas sus instancias de parte que son objetos se eliminan con él.
Pero al mismo tiempo también dicen
Un objeto parcial se puede eliminar de un objeto compuesto antes de que se elimine el objeto compuesto, y por lo tanto no se borrará como parte del objeto compuesto.
Esta confusión apunta a una definición incompleta de UML, que no tiene en cuenta las dependencias del ciclo de vida entre los componentes y los compuestos. Por lo tanto, es importante comprender cómo se puede mejorar la definición UML mediante la introducción de un estereotipo UML para composiciones " inseparables" en las que los componentes no se pueden separar de su compuesto y, por lo tanto, deben destruirse siempre que se destruya su compuesto.
1) Composición
Como Martin Fowler ha explicado , el principal problema para caracterizar la composición es que "un objeto solo puede ser parte de una relación de composición". Esto también se explica en la excelente publicación de blog UML Composition vs Aggregation vs Association por Geert Bellekens. Además de esta característica definitoria de una composición (tener partes exclusivas o no compartibles ), una composición también puede venir con una dependencia del ciclo de vida entre el compuesto y sus componentes. De hecho, hay dos tipos de tales dependencias:
- Cuando un componente siempre debe estar unido a un compuesto, o, en otras palabras, cuando tiene un compuesto obligatorio , como se expresa por la multiplicidad "exactamente una" en el lado compuesto de la línea de composición, entonces debe ser reutilizado en (o volver a unir) a otro compuesto, o destruido, cuando se destruye su compuesto actual. Esto se ejemplifica con la composición entre
Person
yHeart
, que se muestra en el siguiente diagrama. Un corazón es destruido o trasplantado a otra persona, cuando su dueño ha muerto. - Cuando un componente no puede separarse de su compuesto, o, en otras palabras, cuando es inseparable , entonces, y solo entonces, el componente debe ser destruido, cuando se destruye su compuesto. Un ejemplo de tal composición con partes inseparables es la composición entre
Person
yBrain
.
En resumen, las dependencias del ciclo de vida solo se aplican a casos específicos de composición, pero no en general, por lo tanto, no son una característica definitoria.
La especificación UML establece: "Una parte se puede eliminar de una instancia compuesta antes de que se elimine la instancia compuesta y, por lo tanto, no se elimine como parte de la instancia compuesta". En el ejemplo de una composición de Car
y Engine
, como se muestra en el siguiente diagrama, es evidente que el motor puede separarse del automóvil antes de que se destruya el automóvil, en cuyo caso el motor no se destruye y puede reutilizarse. . Esto está implícito en la multiplicidad cero o una en el lado compuesto de la línea de composición.
La multiplicidad del extremo de asociación de una composición en el lado compuesto es 1 o 0..1, dependiendo del hecho de si los componentes tienen un compuesto obligatorio (deben estar unidos a un compuesto) o no. Si los componentes son inseparables , esto implica que tienen un compuesto obligatorio.
2) Agregación
Una agregación es otra forma especial de asociación con el significado pretendido de una relación parte-todo, donde las partes de un todo se pueden compartir con otras totalidades. Por ejemplo, podemos modelar una agregación entre las clases DegreeProgram
y Course
, como se muestra en el siguiente diagrama, ya que un curso es parte de un programa de grado y un curso puede compartirse entre dos o más programas de grado (por ejemplo, un título de ingeniería podría compartir un curso de programación C con un grado de informática).
Sin embargo, el concepto de una agregación con partes compartibles no significa mucho, realmente, por lo que no tiene ninguna implicación en la implementación y, por lo tanto, muchos desarrolladores prefieren no usar el diamante blanco en sus diagramas de clase, sino simplemente modelar una asociación simple en lugar. La especificación de UML dice: "La semántica precisa de la agregación compartida varía según el área de aplicación y el modelador".
La multiplicidad del extremo de asociación de una agregación en todo el lado puede ser cualquier número (*) porque una parte puede pertenecer a, o compartirse entre, cualquier cantidad de totalidades.
La composición implica que los objetos secundarios comparten una vida útil con el padre. La agregación no. Por ejemplo, un tablero de ajedrez se compone de cuadrados de ajedrez: los cuadros de ajedrez no existen realmente sin el tablero. Sin embargo, un automóvil es una agregación de partes: el escape de un automóvil sigue siendo el escape de un automóvil si no es parte de un automóvil en ese momento.
La composición y la agregación son tipos de asociaciones. Están estrechamente relacionados y en términos de programación no parece mucha diferencia. Voy a tratar de explicar la diferencia entre estos dos ejemplos de código java
Agregación : el objeto existe fuera del otro, se crea fuera, por lo que se pasa como un argumento (por ejemplo) al construtor. Ej .: Gente - automóvil. El automóvil se crea en un contexto diferente y luego se convierte en propiedad de una persona.
// code example for Aggregation:
// reference existing HttpListener and RequestProcessor
public class WebServer {
private HttpListener listener;
private RequestProcessor processor;
public WebServer(HttpListener listener, RequestProcessor processor) {
this.listener = listener;
this.processor = processor;
}
}
Composición : el objeto solo existe, o solo tiene sentido dentro del otro, como parte del otro. Ej .: Gente - corazón. No creas un corazón y luego se lo pasa a una persona.
// code example for composition:
// create own HttpListener and RequestProcessor
public class WebServer {
private HttpListener listener;
private RequestProcessor processor;
public WebServer() {
this.listener = new HttpListener(80);
this.processor = new RequestProcessor(“/www/root”);
}
}
Explicado aquí con un ejemplo Diferencia entre agregación y composición
La distinción entre agregación y composición depende del contexto.
Tome el ejemplo del automóvil mencionado en otra respuesta: sí, es cierto que el escape de un automóvil puede sostenerse "solo", por lo que puede no estar en la composición de un automóvil, pero depende de la aplicación. Si construyes una aplicación que realmente tiene que lidiar con escapes de autos independientes (¿una aplicación de gestión de tiendas de autos?), La agregación sería tu elección. Pero si este es un juego de carreras simple y el escape del automóvil solo sirve como parte de un automóvil, bueno, la composición sería bastante buena.
Tablero de ajedrez? El mismo problema. Una pieza de ajedrez no existe sin un tablero de ajedrez solo en ciertas aplicaciones. En otros (como el de un fabricante de juguetes), una pieza de ajedrez seguramente no puede ser compuesta en un tablero de ajedrez.
Las cosas empeoran cuando tratas de asignar composición / agregación a tu lenguaje de programación favorito. En algunos idiomas, la diferencia puede ser más fácil de notar ("por referencia" vs. "por valor", cuando las cosas son simples), pero en otros puede no existir en absoluto.
Y una última palabra de consejo? No pierdas demasiado tiempo en este tema. No vale la pena. La distinción apenas es útil en la práctica (incluso si tiene una "composición" completamente clara, es posible que desee implementarla como una agregación debido a razones técnicas, por ejemplo, el almacenamiento en caché).
Las ilustraciones conceptuales proporcionadas en otras respuestas son útiles, pero me gustaría compartir otro punto que encontré útil.
He obtenido algunos beneficios de UML para la generación de código, para el código fuente o DDL para la base de datos relacional. Allí, he usado la composición para indicar que una tabla tiene una clave foránea que no admite nulos (en la base de datos) y un objeto "padre" (y a menudo "final") que no admite nulos en mi código. Uso la agregación cuando intento que un registro u objeto pueda existir como "huérfano", no asociado a ningún objeto principal, o para ser "adoptado" por un objeto principal diferente.
En otras palabras, he utilizado la notación de composición como una forma abreviada de implicar algunas restricciones adicionales que podrían ser necesarias al escribir el código para el modelo.
Vamos a establecer los términos. La Agregación es un metatermo en el estándar UML, y significa AMBAS composiciones y agregaciones compartidas, simplemente llamadas compartidas . A menudo se denomina incorrectamente "agregación". Es MALO, porque la composición también es una agregación. Según entiendo, te refieres a "compartido".
Más allá del estándar UML:
compuesto: indica que la propiedad se agrega de forma compuesta, es decir, el objeto compuesto es responsable de la existencia y el almacenamiento de los objetos compuestos (partes).
Entonces, la asociación de la Universidad a las cátedras es una composición, porque la cátedra no existe fuera de la Universidad (en mi humilde opinión)
La semántica precisa de la agregación compartida varía según el área de aplicación y el modelador.
Es decir, todas las demás asociaciones pueden dibujarse como agregaciones compartidas, si solo está siguiendo algunos de sus principios o los de alguien más. También mira here .