Агрегация против композиции
Часть раздела Объектно-ориентированное программирование путешествия по Java на Coddy. Урок 48 из 87.
И aggregation, и composition являются формами отношений «has-a», но они различаются по одному ключевому аспекту: владению и зависимости жизненного цикла.
При composition содержащийся объект не может существовать independently от контейнера. Когда контейнер уничтожается, его части тоже уничтожаются. Представь 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 создаёт и владеет rooms
}
}
}В aggregation содержащийся объект может существовать независимо. Контейнер использует объект, но не управляет его жизненным циклом. Рассмотрим Team и его объекты Player: игроки существуют даже без команды:
class Team {
private List<Player> players;
public Team() {
players = new ArrayList<>();
}
public void addPlayer(Player player) {
players.add(player); // Team использует существующих players
}
}Ключевое различие: в composition контейнер создаёт свои части internally. В aggregation объекты передаются извне. Когда удаляется Team, объекты Player по-прежнему существуют. Когда удаляется House, его объекты Room также исчезают.
Задание
ЛегкоДавайте создадим систему университетских курсов, которая демонстрирует aggregation и composition. Вы увидите, как Course владеет своими объектами Lecture (composition), тогда как он лишь ссылается на объекты Professor, которые существуют независимо (aggregation).
Вы организуете код в четырёх файлах:
Professor.java: создайте class, представляющий профессора, который может существовать независимо от любого курса. У Professor есть два private поля:name(String) иdepartment(String). Добавьте constructor для инициализации обоих полей и getter-методы для каждого из них. ПереопределитеtoString(), чтобы он возвращал:Prof. [name] ([department])Lecture.java: создайте class, представляющий лекцию, которая имеет смысл только внутри курса. У Lecture есть два private поля:topic(String) иdurationMinutes(int). Добавьте constructor и getter-методы. ПереопределитеtoString(), чтобы он возвращал:Lecture: [topic] ([durationMinutes] min)Course.java: здесь вы продемонстрируете обе связи! У Course есть три private поля:title(String),professor(Professor — aggregation) иlectures(array объектов Lecture — composition).Constructor принимает название курса, объект Professor (переданный извне — aggregation) и количество лекций. Внутри constructor создайте array Lecture и заполните его новыми объектами Lecture. Для каждой лекции с index
iсоздайте её с topic"Topic " + (i + 1)и duration45минут. Это внутреннее создание демонстрирует composition.Добавьте метод
getInfo(), который возвращает многострочный String:Course: [title] Instructor: [professor.toString()] Lectures: [number of lectures]Также добавьте метод
listLectures(), который возвращает String, где toString() каждой лекции находится на отдельной строке.Main.java: объединим всё вместе! Вы получите четыре inputs: имя профессора (String), department профессора (String), название курса (String) и количество лекций (int).Сначала создайте Professor с указанными именем и department. Этот professor существует независимо. Затем создайте Course, передав title, professor (aggregation) и количество лекций (course создаст собственные lectures internally — composition).
Выведите результат
getInfo(), затем выведите пустую строку, а после этого выведите результатlistLectures().
Вы получите четыре inputs по порядку: имя профессора, department, название курса и количество лекций.
Обратите внимание на ключевое различие: Professor создаётся снаружи и передаётся в Course (aggregation: professor мог бы преподавать на других курсах или существовать без этого курса). Lectures создаются внутри constructor Course (composition). Они принадлежат исключительно этому курсу и не существовали бы без него).
Попробуйте сами
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, передав professor (агрегация)
// Курс создаст свои собственные лекции внутри (композиция)
// 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