Agregación vs. Composición
Parte de la sección Programación Orientada a Objetos del Journey de Java de Coddy. Lección 48 de 87.
Tanto la agregación como la composición son formas de relaciones «tiene un», pero se diferencian en un aspecto crucial: la propiedad y la dependencia del ciclo de vida.
En la composición, el objeto contenido no puede existir independientemente del contenedor. Cuando se destruye el contenedor, también se destruyen sus partes. Piensa en una House y sus objetos Room: las habitaciones no existen sin la casa:
class House {
private Room[] rooms;
public House(int numRooms) {
rooms = new Room[numRooms];
for (int i = 0; i < numRooms; i++) {
rooms[i] = new Room(); // House crea y posee las habitaciones
}
}
}En la agregación, el objeto contenido puede existir de forma independiente. El contenedor usa el objeto, pero no controla su ciclo de vida. Considera un Team y sus objetos Player: los jugadores existen incluso sin un equipo:
class Team {
private List<Player> players;
public Team() {
players = new ArrayList<>();
}
public void addPlayer(Player player) {
players.add(player); // Team utiliza jugadores existentes
}
}La diferencia clave: en la composición, el contenedor crea sus partes internamente. En la agregación, los objetos se pasan desde fuera. Cuando se elimina un Team, los objetos Player siguen existiendo. Cuando se elimina una House, sus objetos Room también desaparecen.
Desafío
FácilCreemos un sistema de cursos universitarios que demuestre tanto la agregación como la composición. Verás cómo un Course posee sus objetos Lecture (composición), mientras que solo hace referencia a objetos Professor que existen de forma independiente (agregación).
Organizarás tu código en cuatro archivos:
Professor.java: Crea una clase que represente a un profesor que pueda existir independientemente de cualquier curso. Un Professor tiene dos campos privados:name(String) ydepartment(String). Incluye un constructor para inicializar ambos campos y métodos getter para cada uno. SobrescribetoString()para que devuelva:Prof. [name] ([department])Lecture.java: Crea una clase que represente una clase lectiva que solo tenga sentido dentro de un curso. Una Lecture tiene dos campos privados:topic(String) ydurationMinutes(int). Incluye un constructor y métodos getter. SobrescribetoString()para que devuelva:Lecture: [topic] ([durationMinutes] min)Course.java: ¡Aquí demostrarás ambas relaciones! Un Course tiene tres campos privados:title(String),professor(Professor - agregación) ylectures(una matriz de objetos Lecture - composición).El constructor recibe el título del curso, un objeto Professor (pasado desde fuera: agregación) y la cantidad de clases lectivas. Dentro del constructor, crea la matriz de Lecture y llénala con nuevos objetos Lecture. Para cada clase lectiva en el índice
i, créala con el tema"Topic " + (i + 1)y una duración de45minutos. Esta creación interna demuestra la composición.Agrega un método
getInfo()que devuelva un String de varias líneas:Course: [title] Instructor: [professor.toString()] Lectures: [number of lectures]Agrega también un método
listLectures()que devuelva un String con el toString() de cada clase lectiva en una línea propia.Main.java: ¡Reúne todo! Recibirás cuatro datos de entrada: el nombre del profesor (String), el departamento del profesor (String), el título del curso (String) y la cantidad de clases lectivas (int).Primero, crea un Professor con el nombre y el departamento proporcionados. Este profesor existe de forma independiente. Después, crea un Course, pasando el título, el profesor (agregación) y la cantidad de clases lectivas (el curso creará sus propias clases lectivas internamente: composición).
Imprime el resultado de
getInfo(), después imprime una línea vacía y, por último, imprime el resultado delistLectures().
Recibirás cuatro datos de entrada en este orden: el nombre del profesor, el departamento, el título del curso y la cantidad de clases lectivas.
Observa la diferencia clave: el Professor se crea fuera y se pasa al Course (agregación: el profesor podría impartir otros cursos o existir sin este). Las Lecture se crean dentro del constructor de Course (composición). Pertenecen exclusivamente a este curso y no existirían sin él).
Pruébalo tú mismo
import java.util.Scanner;
class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// Lee las entradas
String professorName = scanner.nextLine();
String department = scanner.nextLine();
String courseTitle = scanner.nextLine();
int numberOfLectures = scanner.nextInt();
// TODO: Crea un objeto Professor (existe de forma independiente - agregación)
// TODO: Crea un objeto Course y pásale el profesor (agregación)
// El curso creará internamente sus propias clases (composición)
// TODO: Imprime el resultado de getInfo()
// TODO: Imprime una línea en blanco
// TODO: Imprime el resultado de listLectures()
}
}Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.
Todas las lecciones de Programación Orientada a Objetos
1Fundamentos de OOP
Archivos externosIntroducción a la OOPClases vs. ObjetosLa palabra clave thisMétodosCampos (Atributos)Método constructorSobrecarga de constructoresResumen - Calculadora simple4Herencia
Herencia básica (extends)La palabra clave superSobrescritura de métodos (@Override)Encadenamiento de constructoresLa clase ObjectHerencia simple y multinivelPor qué no hay herencia múltiple de clasesResumen - Jerarquía de empleados7Métodos especiales y la clase Object
Método toString()equals() y hashCode()Método clone()compareTo() y ComparableInterfaz ComparatorResumen - Ordenamiento personalizado2Modificadores de acceso y encapsulamiento
Visión general de los niveles de accesoMétodos Getter y SetterOcultamiento de informaciónLa palabra clave finalResumen - Gestor de cuentas bancarias5Polimorfismo
Fundamentos de sobrecarga de métodosSobrescritura de métodos (tiempo de ejecución)Upcasting y DowncastingEl operador instanceofClases y métodos abstractosResumen - Calculadora de figuras8Conceptos avanzados de POO
Composición vs. HerenciaAgregación vs. ComposiciónClases internas, anidadas y anónimasEnums y métodos de EnumRecords (Java 16+)Clases selladas (Java 17+)11Patrones de diseño - Parte 1
Introducción a los patrones de diseñoPatrón SingletonPatrón FactoryPatrón BuilderPatrón ObserverPatrón Strategy3Propiedades de clase y miembros estáticos
Variables de instancia vs. estáticasMétodos estáticosBloques estáticosConstantes (static final)Repaso - Contador y utilidad6Interfaces y Clases Abstractas
Introducción a las InterfacesImplementación de InterfacesImplementación de Múltiples InterfacesMétodos Default y Static en InterfacesClases Abstractas vs InterfacesInterfaces FuncionalesResumen - Sistema de Pagos9Genéricos
Introducción a los genéricosClases genéricasMétodos genéricosParámetros de tipo delimitadosComodines (?, extends, super)Resumen - Contenedor genérico12Patrones de diseño Parte 2
Patrón CommandPatrón AdapterPatrón DecoratorPatrón Template MethodPatrón StatePatrón CompositePatrón IteratorPractica por tu cuenta: Compilador de Java online