어그리게이션 vs 컴포지션
Coddy Java 여정의 객체 지향 프로그래밍 섹션에 포함된 레슨 — 87개 중 48번째.
집합(Aggregation)과 합성(Composition)은 모두 "has-a" 관계의 형태이지만, 한 가지 중요한 측면에서 차이가 있습니다: 소유권과 생명주기 의존성입니다.
합성(composition)에서, 포함된 객체는 컨테이너와 독립적으로 존재할 수 없습니다. 컨테이너가 소멸될 때 그 하위 요소들도 함께 소멸됩니다. 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가 Room들을 생성하고 소유합니다
}
}
}집약(aggregation) 관계에서, 포함된 객체는 독립적으로 존재할 수 있습니다. 컨테이너는 객체를 사용하지만 그 생명주기를 제어하지는 않습니다. Team과 그 Player 객체들을 생각해 보세요. 선수들은 팀이 없어도 존재합니다:
class Team {
private List<Player> players;
public Team() {
players = new ArrayList<>();
}
public void addPlayer(Player player) {
players.add(player); // Team은 기존 Player들을 사용합니다
}
}핵심적인 차이점은 다음과 같습니다. 합성(composition)에서는 컨테이너가 내부적으로 부품을 생성합니다. 집약(aggregation)에서는 객체가 외부에서 전달됩니다. Team이 삭제되어도 Player 객체는 여전히 존재합니다. House가 삭제되면 그 안의 Room 객체들도 함께 사라집니다.
챌린지
쉬움집계(aggregation)와 구성(composition)을 모두 보여주는 대학 강의 시스템을 구축해 보겠습니다. Course가 Lecture 객체를 소유하는 방식(구성)과, 독립적으로 존재하는 Professor 객체를 단순히 참조하는 방식(집계)의 차이를 배우게 됩니다.
코드는 네 개의 파일로 구성됩니다:
Professor.java: 어떤 강의와도 독립적으로 존재할 수 있는 교수를 나타내는 클래스를 생성합니다. Professor는 두 개의 private 필드name(String)과department(String)를 가집니다. 두 필드를 초기화하는 생성자와 각 필드에 대한 getter 메서드를 포함하세요.toString()을 오버라이드하여"Prof. " + name + " (" + department + ")"형식을 반환하도록 합니다.Lecture.java: 강의 내에서만 의미가 있는 수업(lecture)을 나타내는 클래스를 생성합니다. Lecture는 두 개의 private 필드topic(String)과durationMinutes(int)를 가집니다. 생성자와 getter를 포함하세요.toString()을 오버라이드하여"Lecture: " + topic + " (" + durationMinutes + " min)"형식을 반환하도록 합니다.Course.java: 여기서 두 가지 관계를 모두 보여줍니다! Course는 세 개의 private 필드title(String),professor(Professor - 집계), 그리고lectures(Lecture 객체 배열 - 구성)를 가집니다.생성자는 강의 제목(title), Professor 객체(외부에서 전달됨 - 집계), 그리고 강의 수(number of lectures)를 매개변수로 받습니다. 생성자 내부에서 Lecture 배열을 생성하고 새로운 Lecture 객체들로 채웁니다. 인덱스
i에 있는 각 강의에 대해, 주제는"Topic " + (i + 1)로, 기간은45분으로 설정하여 생성합니다. 이 내부적인 생성이 구성을 보여줍니다.다음과 같은 여러 줄의 문자열을 반환하는
getInfo()메서드를 추가하세요:Course: [title] Instructor: [professor.toString()] Lectures: [number of lectures]또한 각 강의의 toString() 결과를 각각 별도의 줄에 포함하는 문자열을 반환하는
listLectures()메서드를 추가하세요.Main.java: 모든 것을 하나로 합칩니다! 교수 이름 (String), 교수 학과 (String), 강의 제목 (String), 강의 수 (int)의 네 가지 입력을 받습니다.먼저, 주어진 이름과 학과로 Professor 객체를 생성합니다. 이 교수는 독립적으로 존재합니다. 그런 다음 제목, 교수 객체(집계), 강의 수(강의는 내부적으로 자신의 수업들을 생성함 - 구성)를 전달하여 Course 객체를 생성합니다.
getInfo()의 결과를 출력하고, 빈 줄을 하나 출력한 다음,listLectures()의 결과를 출력하세요.
교수 이름, 학과, 강의 제목, 강의 수 순서대로 네 개의 입력을 받게 됩니다.
핵심적인 차이점에 주목하세요: Professor는 외부에서 생성되어 Course로 전달됩니다(집계 — 교수는 다른 강의를 가르칠 수도 있고 이 강의 없이도 존재할 수 있습니다). Lectures는 Course 생성자 내부에서 생성됩니다(구성 — 이들은 오직 이 강의에만 속하며 강의 없이는 존재하지 않습니다).
치트 시트
집합(Aggregation)과 합성(Composition)은 모두 소유권과 생명주기 의존성이 다른 "has-a" 관계입니다.
합성(Composition): 포함된 객체는 컨테이너와 독립적으로 존재할 수 없습니다. 컨테이너가 소멸될 때 그 구성 요소들도 함께 소멸됩니다. 컨테이너는 내부적으로 구성 요소들을 생성합니다.
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가 방들을 생성하고 소유합니다
}
}
}집합(Aggregation): 포함된 객체는 독립적으로 존재할 수 있습니다. 컨테이너는 객체를 사용하지만 그 생명주기를 제어하지는 않습니다. 객체는 외부에서 전달됩니다.
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: Professor를 전달하여 Course 객체 생성 (집합 관계)
// Course는 내부적으로 자체 강의를 생성함 (합성 관계)
// TODO: getInfo() 결과 출력
// TODO: 빈 줄 출력
// TODO: listLectures() 결과 출력
}
}이 레슨에는 짧은 퀴즈가 포함되어 있습니다. 레슨을 시작해 문제를 풀고 진행 상황을 기록하세요.
객체 지향 프로그래밍의 모든 레슨
4상속
상속의 기초 (extends)super 키워드메서드 오버라이딩 (@Override)생성자 체이닝Object 클래스단일 및 다중 레벨 상속다중 클래스 상속이 불가능한 이유요약 - 직원 계층 구조