集約 vs コンポジション
CoddyのJavaジャーニー「オブジェクト指向プログラミング」セクションの一部。レッスン 48/87。
集約とコンポジションはいずれも「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オブジェクトを参照するだけである(集約)ことを確認します。
コードを4つのファイルに分けて構成します。
Professor.java:どのコースにも属さずに独立して存在できる教授を表すクラスを作成します。Professorには、name(String)とdepartment(String)の2つのprivateフィールドがあります。両方のフィールドを初期化するコンストラクターと、それぞれのgetterメソッドを含めてください。toString()をオーバーライドし、次を返すようにします:Prof. [name] ([department])Lecture.java:コースの中でのみ意味を持つ講義を表すクラスを作成します。Lectureには、topic(String)とdurationMinutes(int)の2つのprivateフィールドがあります。コンストラクターとgetterを含めてください。toString()をオーバーライドし、次を返すようにします:Lecture: [topic] ([durationMinutes] min)Course.java:ここで両方の関係を示します!Courseには、title(String)、professor(Professor—集約)、lectures(Lectureオブジェクトの配列—コンポジション)の3つのprivateフィールドがあります。コンストラクターは、コース名、Professorオブジェクト(外部から渡される—集約)、講義の数を受け取ります。コンストラクター内でLecture配列を作成し、新しいLectureオブジェクトを格納します。インデックス
iの各講義について、トピックを"Topic " + (i + 1)、時間を45分として作成します。この内部での作成によって、コンポジションを示します。複数行のStringを返す
getInfo()メソッドを追加します。Course: [title] Instructor: [professor.toString()] Lectures: [number of lectures]また、各講義のtoString()をそれぞれ別の行に含むStringを返す
listLectures()メソッドも追加します。Main.java:すべてをまとめましょう!次の4つの入力を受け取ります:教授の名前(String)、教授の学部(String)、コース名(String)、講義の数(int)。まず、指定された名前と学部でProfessorを作成します。この教授は独立して存在します。次にCourseを作成し、コース名、教授(集約)、講義の数(コースが内部で独自の講義を作成します—コンポジション)を渡します。
getInfo()の結果を出力し、次に空行を出力してから、listLectures()の結果を出力します。
入力は、教授の名前、学部、コース名、講義の数の順に4つ受け取ります。
重要な違いに注目してください。Professorは外部で作成され、Courseに渡されます(集約:教授は他のコースを教えることも、このコースがなくても存在することもできます)。LectureはCourseのコンストラクター内で作成されます(コンポジション:Lectureはこのコースだけに属し、このコースがなければ存在しません)。
自分で試してみよう
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オンラインコンパイラ