Агрегация против композиции
Часть раздела Объектно-ориентированное программирование путешествия по Java на Coddy. Урок 48 из 87.
И агрегация, и композиция — это формы отношений «имеет», но они различаются одним важным аспектом: владением и зависимостью жизненного цикла.
При композиции содержащийся объект не может существовать независимо от контейнера. Когда контейнер уничтожается, его части тоже уничтожаются. Представь дом 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 использует уже существующих игроков
}
}Ключевое различие: при композиции контейнер создаёт свои части внутри себя. При агрегации объекты передаются извне. Когда Team удаляется, объекты Player всё ещё существуют. Когда удаляется House, его объекты Room тоже исчезают.
Задание
ЛегкоДавайте создадим систему университетских курсов, которая демонстрирует агрегацию и композицию. Вы увидите, как Course владеет объектами Lecture (композиция), а объекты Professor только связываются с курсом и существуют независимо (агрегация).
Вы распределите код по четырём файлам:
Professor.java: Создайте класс, представляющий профессора, который может существовать независимо от любого курса. У профессора есть два приватных поля:name(String) иdepartment(String). Добавьте конструктор для инициализации обоих полей и методы получения значений для каждого из них. ПереопределитеtoString(), чтобы он возвращал:Prof. [name] ([department])Lecture.java: Создайте класс, представляющий лекцию, которая имеет смысл только в рамках курса. У лекции есть два приватных поля:topic(String) иdurationMinutes(int). Добавьте конструктор и методы получения значений. ПереопределитеtoString(), чтобы он возвращал:Lecture: [topic] ([durationMinutes] min)Course.java: Именно здесь вы продемонстрируете оба типа связей! У курса есть три приватных поля: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 (агрегация: профессор может вести другие курсы или существовать без этого курса). Lectures создаются внутри конструктора Course (композиция). Они принадлежат исключительно этому курсу и не существовали бы без него.
Попробуйте сами
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
Паттерн «Команда»Паттерн «Адаптер»Паттерн «Декоратор»Паттерн «Шаблонный метод»Паттерн «Состояние»Паттерн «Компоновщик»Паттерн «Итератор»Потренируйтесь самостоятельно: Онлайн-компилятор Java