En informática , la composición de objetos y la agregación de objetos son formas estrechamente relacionadas de combinar objetos o tipos de datos en otros más complejos. En la conversación a menudo se ignora la distinción entre composición y agregación. [1] Los tipos comunes de composiciones son objetos utilizados en programación orientada a objetos , uniones etiquetadas , conjuntos , secuencias y diversas estructuras gráficas . Las composiciones de objetos se relacionan con las estructuras de datos, pero no son lo mismo.
La composición del objeto se refiere a la estructura lógica o conceptual de la información, no a la implementación o estructura de datos físicos utilizados para representarla [ cita necesaria ] . Por ejemplo, una secuencia se diferencia de un conjunto porque (entre otras cosas) el orden de los elementos compuestos importa para la primera pero no para el segundo. Se pueden utilizar estructuras de datos como matrices , listas vinculadas , tablas hash y muchas otras para implementar cualquiera de ellas. Quizás resulte confuso que algunos de los mismos términos se utilicen tanto para estructuras de datos como para compuestos. Por ejemplo, " árbol binario " puede referirse a: como estructura de datos, es un medio para acceder a una secuencia lineal de elementos, y las posiciones reales de los elementos en el árbol son irrelevantes (el árbol se puede reorganizar internamente como uno quiera). sin cambiar su significado). Sin embargo, como composición de objetos, las posiciones son relevantes y cambiarlas cambiaría el significado (como por ejemplo en los cladogramas ) [ cita necesaria ] .
La programación orientada a objetos se basa en objetos para encapsular datos y comportamiento. Utiliza dos técnicas principales para ensamblar y componer funcionalidades en otras más complejas: subtipificación y composición de objetos. [2] La composición de objetos consiste en combinar objetos dentro de objetos compuestos y, al mismo tiempo, garantizar la encapsulación de cada objeto mediante el uso de su interfaz bien definida sin visibilidad de sus partes internas. En este sentido, la composición de objetos difiere de las estructuras de datos, que no imponen la encapsulación.
La composición de objetos también puede tratarse de un grupo de múltiples objetos relacionados, como un conjunto o una secuencia de objetos. La delegación puede enriquecer la composición reenviando solicitudes o llamadas realizadas al objeto compuesto adjunto a uno de sus componentes internos. [3]
En los lenguajes de programación basados en clases y tipificados , los tipos se pueden dividir en tipos compuestos y no compuestos, y la composición se puede considerar como una relación entre tipos: un objeto de un tipo compuesto (por ejemplo, un automóvil ) " tiene " objetos de otros tipos ( por ejemplo, rueda ). Cuando un objeto compuesto contiene varios subobjetos del mismo tipo, se les puede asignar roles particulares , a menudo distinguidos por nombres o números. Por ejemplo, un objeto Punto puede contener 3 números, cada uno de los cuales representa la distancia a lo largo de un eje diferente, como 'x', 'y' y 'z'. El estudio de las relaciones parte-todo en general, es mereología .
La composición debe distinguirse de la subtipificación , que es el proceso de agregar detalles a un tipo de datos general para crear un tipo de datos más específico. Por ejemplo, los automóviles pueden ser un tipo específico de vehículo: el automóvil es un vehículo . La subtipificación no describe una relación entre diferentes objetos, sino que dice que los objetos de un tipo son simultáneamente objetos de otro tipo. El estudio de tales relaciones es la ontología .
En lenguajes de programación basados en prototipos como JavaScript , los objetos pueden heredar dinámicamente los comportamientos de un objeto prototipo en el momento de su instanciación. La composición debe distinguirse de la creación de prototipos: el objeto recién instanciado hereda la composición de su prototipo, pero él mismo puede componerse por sí solo.
Los objetos compuestos se pueden representar en el almacenamiento coubicando los objetos compuestos, coubicando referencias o de muchas otras maneras. Los elementos dentro de un objeto compuesto pueden denominarse atributos , campos , miembros , propiedades u otros nombres, y la composición resultante como tipo compuesto , registro de almacenamiento , estructura , tupla o un tipo definido por el usuario (UDT) . Para obtener más información, consulte la sección de agregación a continuación.
En el modelado UML , los objetos se pueden componer conceptualmente, independientemente de la implementación con un lenguaje de programación. Hay cuatro formas de componer objetos en UML: propiedad, asociación, agregación y composición: [4]
La relación entre el agregado y sus componentes es una relación débil "tiene-a": los componentes pueden ser parte de varios agregados, se puede acceder a ellos a través de otros objetos sin pasar por el agregado y pueden sobrevivir al objeto agregado. [4] El estado del objeto componente todavía forma parte del objeto agregado. [ cita necesaria ]
La relación entre el compuesto y sus partes es una fuerte relación "tiene-un": el objeto compuesto tiene la " responsabilidad exclusiva de la existencia y almacenamiento de los objetos compuestos ", el objeto compuesto puede ser parte de como máximo un compuesto, y " Si se elimina un objeto compuesto, todas sus instancias parciales que son objetos se eliminan con él ". Por tanto, en UML, composición tiene un significado más limitado que la composición de objetos habitual.
La notación gráfica representa:
La agregación se diferencia de la composición ordinaria en que no implica propiedad. En la composición, cuando el objeto propietario se destruye, también se destruyen los objetos contenidos. En conjunto, esto no es necesariamente cierto. Por ejemplo, una universidad posee varios departamentos (por ejemplo, química ) y cada departamento tiene varios profesores. Si la universidad cierra, los departamentos ya no existirán, pero los profesores de esos departamentos seguirán existiendo. Por lo tanto, una universidad puede verse como una composición de departamentos, mientras que los departamentos tienen una agregación de profesores. Además, un profesor podría trabajar en más de un departamento, pero un departamento no podría formar parte de más de una universidad.
La composición generalmente se implementa de manera que un objeto contenga otro objeto. Por ejemplo, en C++ :
profesor de clase ; // Definido en otro lugar clase Departamento { público : Departamento ( const std :: cadena y título ) : título_ ( título ) {} privado : // Agregación: |Profesores| puede sobrevivir al |Departamento|. std :: vector < std :: débil_ptr < Profesor >> miembros_ ; const std :: cadena título_ ; }; clase Universidad { pública : Universidad () = predeterminado ; privado : // Composición: |Departamento|s existen sólo mientras existe la facultad. std :: vector < Departamento > facultad_ = { Departamento ( "química" ), Departamento ( "física" ), Departamento ( "artes" ), }; };
En agregación, el objeto solo puede contener una referencia o un puntero al objeto (y no tener responsabilidad de por vida sobre él).
A veces se hace referencia a la agregación como composición cuando la distinción entre composición ordinaria y agregación no es importante.
El código anterior se transformaría en el siguiente diagrama de clases UML:
En el Modelo de objetos componentes de Microsoft , agregación significa que un objeto exporta, como si fuera su propietario, una o varias interfaces de otro objeto de su propiedad. Formalmente, esto es más similar a la composición o encapsulación que a la agregación. Sin embargo, en lugar de implementar las interfaces exportadas llamando a las interfaces del objeto de propiedad, se exportan las interfaces del objeto de propiedad. El objeto de propiedad es responsable de garantizar que los métodos de aquellas interfaces heredadas de IUnknown realmente invoquen los métodos correspondientes del propietario. Esto es para garantizar que el recuento de referencias del propietario sea correcto y que todas las interfaces del propietario sean accesibles a través de la interfaz exportada, mientras que no se pueda acceder a otras interfaces (privadas) del objeto de propiedad. [5]
La composición que se utiliza para almacenar varias instancias del tipo de datos compuestos se denomina contención. Ejemplos de dichos contenedores son matrices , matrices asociativas , árboles binarios y listas enlazadas .
En UML , la contención se representa con una multiplicidad de 0..* o 1..*, lo que indica que el objeto compuesto está compuesto por un número desconocido de instancias de la clase compuesta.
Los objetos se pueden componer de forma recursiva y su tipo se denomina tipo recursivo . Los ejemplos incluyen varios tipos de árboles , DAG y gráficos . Cada nodo de un árbol puede ser una rama o una hoja; es decir, cada nodo es un árbol al mismo tiempo que pertenece a otro árbol.
En UML, la composición recursiva se representa con una asociación, agregación o composición de una clase consigo misma.
El patrón de diseño compuesto es un diseño orientado a objetos basado en tipos compuestos, que combina composición recursiva y contención para implementar jerarquías complejas de parte y todo.
Este es un ejemplo de composición en C.
estructura Persona { int edad ; nombre de carácter [ 20 ]; enum { búsqueda de empleo , profesional , no profesional , jubilado , estudiante } empleo ; };
En este ejemplo, los tipos primitivos (no compuestos) int , enum {busca_trabajo, profesional, no_profesional, jubilado, estudiante } y el tipo de matriz compuesta char[] se combinan para formar la estructura compuesta Persona . Cada estructura de Persona "tiene" una edad, un nombre y un tipo de empleo.
C llama a un registro estructura o estructura; Los lenguajes orientados a objetos como Java , Smalltalk y C++ a menudo mantienen sus registros ocultos dentro de los objetos ( instancias de clase ); Los lenguajes de la familia ML simplemente los llaman registros. COBOL fue el primer lenguaje de programación generalizado que admitió registros directamente; [6] ALGOL 68 lo obtuvo de COBOL y Pascal lo obtuvo, más o menos indirectamente, de ALGOL 68. Common Lisp proporciona estructuras y clases (estas últimas a través del Common Lisp Object System ). [ cita necesaria ]
01 registro-cliente . 03 número de cliente foto 9(8) comp . 03 nombre-cliente . 05 nombres de pila pic x(15) . 05 inicial-2 foto x . 05 foto de apellido x(15) . 03 dirección-cliente . calle 05 . 07 foto del nombre de la calle x(15) . 09 foto del número de casa 999 comp . 05 foto de la ciudad x(10) . 05 imagen de código de país x(3) . 05 imagen de código postal x(8) . 03 monto adeudado imagen 9(8) comp .
Las matrices eran el único tipo de datos compuestos en Algol 60 .
dcl 1 basado en nuevo tipo (P); 2 (a, b, c) contenedor fijo (31), 2 (i, j, k) flotar, 2 r pto;asignar nuevo tipo;
int máx = 99;modo nuevotipo = [0..9] [0..max]struct ( largo real a, b, c, corto int i, j, k, ref real r);newtypet newarrayt = (1, 2, 3, 4, 5, 6, montón real := 7)
Por ejemplo, una lista enlazada podría declararse como:
nodo de modo = unión (real, int, compl, cadena), lista = estructura (valor de nodo, lista de referencia siguiente);
Para ALGOL 68, solo el nombre del tipo aparece a la izquierda de la igualdad y, lo más notable, la construcción se realiza (y se puede leer) de izquierda a derecha sin tener en cuenta las prioridades.
escriba a = matriz [ 1 .. 10 ] de número entero ; b = registro a , b , c : real ; i , j , k : entero ; fin ;
#define max 99 struct newtypet { doble a , b , c ; flotador r ; corto i , j , k ; } newarrayt [ 10 ] [ máx + 1 ];
Fortran 77 tiene matrices, pero carecía de definiciones formales de registro/estructura. Normalmente, las estructuras compuestas se construían utilizando declaraciones EQUIVALENCIA o COMÚN :
NOMBRE DEL PERSONAJE * 32 , DIRECCIÓN * 32 , TELÉFONO * 16 REAL DEBIDO COMÚN / CLIENTE / NOMBRE , DIRECCIÓN , TELÉFONO , DEBIDO
tipo Cust es nombre de registro : Nombre_Tipo ; Dirección : Tipo_Dirección ; Teléfono : Tipo_teléfono ; Debido : rango de números enteros 1. . 999999 ; registro final ;
Ada 95 trajo conceptos de programación orientada a objetos a través de tipos etiquetados (el equivalente a una clase C++), Ada 2012 agregó soporte para la verificación de sustitución a través de contratos de toda la clase.
constante int máx = 99 ; clase { público : doble a , b , c ; flotador & r ; corto i , j , k ; } nuevotipo [ 10 ] [ máx + 1 ];
max = 99 clase NewTypeT : def __init__ ( self ): self . a = yo . b = yo . c = 0 uno mismo . yo = yo . j = yo . k = 0.0 # Inicializa una matriz de ejemplo de esta clase. newarrayt = [[ NewTypeT () para i en el rango ( max + 1 )] para j en el rango ( 10 )]
Las matrices y cadenas se heredaron de FORTRAN 77 y se introdujo una nueva palabra reservada: tipo
tipo newtypet doble precisión a , b , c entero * 2 i , j , k * Sin tipo de puntero REF REAL R tipo final escriba ( nuevotipo ) t ( 10 , 100 )
FORTRAN 90 actualizó e incluyó el concepto de FORTRAN IV llamado NAMELIST.
INTEGER :: ene = 1 , feb = 2 , mar = 3 , abr = 4 LISTA DE NOMBRES / semana / ene , feb , mar , abr
Common Lisp proporciona estructuras y el estándar ANSI Common Lisp agrega clases CLOS.
( defclass alguna clase () (( f :tipo flotante ) ( i :tipo entero ) ( a :tipo ( matriz entero ( 10 )))))
Para obtener más detalles sobre la composición en C/C++, consulte Tipo compuesto .
Existe un concepto estrechamente relacionado con la composición llamado agregación. En una conversación, a menudo se ignoran las diferencias entre composición y agregación.
{{cite book}}
: Mantenimiento CS1: otros ( enlace )