Menu
Coddy logo textTech

Агрегация против композиции

Часть раздела Объектно-ориентированное программирование путешествия по 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 также исчезают.

challenge icon

Задание

Легко

Давайте создадим систему университетских курсов, которая демонстрирует 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) и duration 45 минут. Это внутреннее создание демонстрирует 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()
        
    }
}
quiz iconПроверьте себя

В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.

Все уроки раздела Объектно-ориентированное программирование

Потренируйтесь самостоятельно: Онлайн-компилятор Java