어그리게이션 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 객체도 함께 사라집니다.
챌린지
쉬움집약과 합성을 모두 보여 주는 대학 강의 시스템을 만들어 봅시다. 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() 결과 출력
}
}이 레슨에는 짧은 퀴즈가 포함되어 있습니다. 레슨을 시작해 문제를 풀고 진행 상황을 기록하세요.
객체 지향 프로그래밍의 모든 레슨
4상속
상속의 기초 (extends)super 키워드메서드 오버라이딩 (@Override)생성자 체이닝Object 클래스단일 및 다중 레벨 상속다중 클래스 상속이 불가능한 이유요약 - 직원 계층 구조직접 연습해 보세요: 온라인 Java 컴파일러