Menu
CoddyTech

어그리게이션 vs 컴포지션

Coddy Java 여정의 객체 지향 프로그래밍 섹션에 포함된 레슨. 87개 중 48번째.

집합과 합성은 모두 "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가 rooms를 생성하고 소유합니다
        }
    }
}

집합에서는 포함된 객체가 독립적으로 존재할 수 있습니다. 컨테이너는 객체를 사용하지만 객체의 생명주기를 제어하지는 않습니다. Team과 그 Player 객체를 생각해 보세요. 플레이어는 팀이 없어도 존재합니다.

class Team {
    private List<Player> players;
    
    public Team() {
        players = new ArrayList<>();
    }
    
    public void addPlayer(Player player) {
        players.add(player);  // Team은 기존 players를 사용합니다
    }
}

핵심적인 차이점은 합성에서는 컨테이너가 내부적으로 구성 요소를 만든다는 점입니다. 집합에서는 객체가 외부에서 전달됩니다. Team이 삭제되어도 Player 객체는 여전히 존재합니다. House가 삭제되면 그 Room 객체도 함께 사라집니다.

challenge icon

챌린지

쉬움

집약과 합성을 모두 보여 주는 대학 강의 시스템을 만들어 봅시다. Course가 자신의 Lecture 객체를 소유하는 방식(합성)과 독립적으로 존재하는 Professor 객체를 참조만 하는 방식(집약)을 살펴보겠습니다.

코드를 네 개의 파일로 나누어 작성합니다.

  • Professor.java: 어떤 강의와도 독립적으로 존재할 수 있는 교수를 나타내는 클래스를 만듭니다. Professor에는 비공개 필드 두 개가 있습니다. name(String)과 department(String)입니다. 두 필드를 모두 초기화하는 생성자와 각 필드의 getter 메서드를 작성합니다. toString()을 재정의하여 다음을 반환하도록 합니다: Prof. [name] ([department])
  • Lecture.java: 강의 안에서만 의미가 있는 강의를 나타내는 클래스를 만듭니다. Lecture에는 비공개 필드 두 개가 있습니다. topic(String)과 durationMinutes(int)입니다. 생성자와 getter를 작성합니다. toString()을 재정의하여 다음을 반환하도록 합니다: Lecture: [topic] ([durationMinutes] min)
  • Course.java: 여기에서 두 관계를 모두 보여 줍니다! Course에는 비공개 필드 세 개가 있습니다. title(String), professor(Professor - 집약), lectures(Lecture 객체 배열 - 합성)입니다.

    생성자는 강의 제목, Professor 객체(외부에서 전달됨 - 집약), 강의 수를 받습니다. 생성자 내부에서 Lecture 배열을 만들고 새 Lecture 객체로 채웁니다. 인덱스 i에 있는 각 강의는 주제 "Topic " + (i + 1)과 길이 45분으로 생성합니다. 이 내부 생성이 합성을 보여 줍니다.

    여러 줄의 String을 반환하는 getInfo() 메서드를 추가합니다.

    Course: [title]
    Instructor: [professor.toString()]
    Lectures: [number of lectures]

    또한 각 강의의 toString()을 별도의 줄에 담은 String을 반환하는 listLectures() 메서드도 추가합니다.

  • Main.java: 모든 것을 하나로 연결합니다! 입력 네 개를 받습니다. 교수 이름(String), 교수 소속 학과(String), 강의 제목(String), 강의 수(int)입니다.

    먼저, 주어진 이름과 소속 학과로 Professor를 만듭니다. 이 교수는 독립적으로 존재합니다. 그런 다음 제목, Professor(집약), 강의 수(강의는 Course 내부에서 자체적으로 생성됨 - 합성)를 전달하여 Course를 만듭니다.

    getInfo()의 결과를 출력한 다음 빈 줄을 출력하고, 이어서 listLectures()의 결과를 출력합니다.

입력은 교수 이름, 소속 학과, 강의 제목, 강의 수 순서로 네 개를 받습니다.

핵심적인 차이에 주목하세요. Professor는 외부에서 생성되어 Course에 전달됩니다(집약: 교수는 다른 강의도 가르칠 수 있으며, 이 강의가 없어도 존재할 수 있습니다). Lecture들은 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: professor를 전달하여 Course 객체 생성 (집약 관계)
        // Course는 내부에서 자체 강의를 생성함 (합성 관계)
        
        // TODO: getInfo() 결과 출력
        
        // TODO: 빈 줄 출력
        
        // TODO: listLectures() 결과 출력
        
    }
}
quiz icon실력 점검

이 레슨에는 짧은 퀴즈가 포함되어 있습니다. 레슨을 시작해 문제를 풀고 진행 상황을 기록하세요.

객체 지향 프로그래밍의 모든 레슨

직접 연습해 보세요: 온라인 Java 컴파일러