Агрегация против композиции
Часть раздела Объектно-ориентированное программирование путешествия по Java на Coddy — урок 48 из 87.
И агрегация, и композиция являются формами отношений «has-a», но они различаются в одном важном аспекте: владение и зависимость жизненного цикла.
При композиции содержащийся объект не может существовать независимо от контейнера. При уничтожении контейнера уничтожаются и его части. Представьте класс House и его объекты Room — комнаты не существуют без дома:
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 создает комнаты и владеет ими
}
}
}При агрегации содержащийся объект может существовать независимо. Контейнер использует объект, но не управляет его жизненным циклом. Рассмотрим объект Team и его объекты Player — игроки существуют даже без команды:
class Team {
private List<Player> players;
public Team() {
players = new ArrayList<>();
}
public void addPlayer(Player player) {
players.add(player); // Команда использует существующих игроков
}
}Ключевое различие: при композиции контейнер создает свои части внутри. При агрегации объекты передаются извне. Когда объект Team удаляется, объекты Player все еще существуют. Когда объект House удаляется, его объекты Room также исчезают.
Задание
ЛегкоДавайте создадим систему университетских курсов, которая демонстрирует как агрегацию, так и композицию. Вы увидите, как Course владеет своими объектами Lecture (композиция), в то время как он только ссылается на объекты Professor, которые существуют независимо (агрегация).
Вы организуете свой код в четырех файлах:
Professor.java: Создайте класс, представляющий профессора, который может существовать независимо от любого курса. У Professor есть два приватных поля:name(String) иdepartment(String). Включите конструктор для инициализации обоих полей и методы-геттеры для каждого. ПереопределитеtoString(), чтобы он возвращал:"Prof. " + name + " (" + department + ")"Lecture.java: Создайте класс, представляющий лекцию, которая имеет смысл только внутри курса. У Lecture есть два приватных поля:topic(String) иdurationMinutes(int). Включите конструктор и геттеры. ПереопределитеtoString(), чтобы он возвращал:"Lecture: " + topic + " (" + durationMinutes + " min)"Course.java: Здесь вы продемонстрируете оба типа отношений! У Course есть три приватных поля:title(String),professor(Professor — агрегация) иlectures(массив объектов Lecture — композиция).Конструктор принимает название курса, объект Professor (переданный извне — агрегация) и количество лекций. Внутри конструктора создайте массив Lecture и заполните его новыми объектами Lecture. Для каждой лекции с индексом
iсоздайте её с темой"Topic " + (i + 1)и продолжительностью45минут. Это внутреннее создание демонстрирует композицию.Добавьте метод
getInfo(), который возвращает многострочную строку:Course: [title] Instructor: [professor.toString()] Lectures: [number of lectures]Также добавьте метод
listLectures(), который возвращает строку, гдеtoString()каждой лекции находится на отдельной строке.Main.java: Соберите всё вместе! Вы получите четыре входных значения: имя профессора (String), кафедра профессора (String), название курса (String) и количество лекций (int).Сначала создайте объект Professor с заданным именем и кафедрой — этот профессор существует независимо. Затем создайте Course, передав название, профессора (агрегация) и количество лекций (курс создаст свои лекции внутри себя — композиция).
Выведите результат
getInfo(), затем выведите пустую строку, а затем выведите результатlistLectures().
Вы получите четыре входных значения по порядку: имя профессора, кафедра, название курса и количество лекций.
Обратите внимание на ключевое различие: Professor создается извне и передается в Course (агрегация — профессор может преподавать другие курсы или существовать без этого). Объекты Lecture создаются внутри конструктора Course (композиция — они принадлежат исключительно этому курсу и не существовали бы без него).
Шпаргалка
Как агрегация, так и композиция являются отношениями типа «has-a» (имеет), которые различаются владением и зависимостью жизненного цикла.
Композиция: содержащийся объект не может существовать независимо от контейнера. Когда контейнер уничтожается, его части также уничтожаются. Контейнер создает свои части внутри себя.
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 создает комнаты и владеет ими
}
}
}Агрегация: содержащийся объект может существовать независимо. Контейнер использует объект, но не управляет его жизненным циклом. Объекты передаются извне.
class Team {
private List<Player> players;
public Team() {
players = new ArrayList<>();
}
public void addPlayer(Player player) {
players.add(player); // Team использует существующих игроков
}
}Ключевое отличие: при удалении контейнера агрегированные объекты продолжают существовать, а композитные объекты уничтожаются.
Попробуйте сами
import java.util.Scanner;
class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// Чтение входных данных
String professorName = scanner.nextLine();
String department = scanner.nextLine();
String courseTitle = scanner.nextLine();
int numberOfLectures = scanner.nextInt();
// TODO: Создать объект Professor (существует независимо - агрегация)
// TODO: Создать объект Course, передав профессора (агрегация)
// Курс будет создавать свои собственные лекции внутри (композиция)
// TODO: Вывести результат getInfo()
// TODO: Вывести пустую строку
// TODO: Вывести результат listLectures()
}
}В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Объектно-ориентированное программирование
1Основы ООП
Внешние файлыВведение в ООПКлассы и объектыКлючевое слово thisМетодыПоля (атрибуты)Метод-конструкторПерегрузка конструкторовИтоги — Простой калькулятор4Наследование
Основы наследования (extends)Ключевое слово superПереопределение методов (@Override)Цепочка конструкторовКласс ObjectОдиночное и многоуровневое наследованиеПочему нет множественного наследования классовИтоги — Иерархия сотрудников7Специальные методы и класс Object
Метод toString()equals() и hashCode()Метод clone()compareTo() и ComparableИнтерфейс ComparatorПовторение — Кастомная сортировка2Модификаторы доступа и инкапсуляция
Обзор уровней доступаМетоды геттеры и сеттерыСокрытие данныхКлючевое слово finalИтоги — Менеджер банковского счета5Полиморфизм
Основы перегрузки методовПереопределение методов (Run-Time)Upcasting и DowncastingОператор instanceofАбстрактные классы и методыИтоги: Калькулятор фигур8Продвинутые концепции ООП
Композиция против наследованияАгрегация против композицииВнутренние, вложенные и анонимные классыEnums и методы перечисленийRecords (Java 16+)Sealed-классы (Java 17+)11Паттерны проектирования. Часть 1
Введение в паттерны проектированияПаттерн SingletonПаттерн FactoryПаттерн BuilderПаттерн ObserverПаттерн Strategy3Свойства класса и статические члены
Переменные экземпляра и статические переменныеСтатические методыСтатические блокиКонстанты (static final)Итоги: Счетчик и утилиты6Интерфейсы и абстрактные классы
Введение в интерфейсыРеализация интерфейсовРеализация нескольких интерфейсовDefault и Static методы в интерфейсахАбстрактные классы vs ИнтерфейсыФункциональные интерфейсыИтоги — Платежная система9Обобщения
Введение в обобщенияОбобщенные классыОбобщенные методыОграниченные параметры типаСимволы подстановки (Wildcards: ?, extends, super)Итоги — Обобщенный контейнер12Паттерны проектирования. Часть 2
Паттерн «Команда»Паттерн «Адаптер»Паттерн «Декоратор»Паттерн «Шаблонный метод»Паттерн «Состояние»Паттерн «Компоновщик»Паттерн «Итератор»