Menu
Coddy logo textTech

集約 vs コンポジション

CoddyのJavaジャーニー「オブジェクト指向プログラミング」セクションの一部。レッスン 48/87。

both aggregation と composition はどちらも「has-a」関係の形ですが、1つの重要な点、つまり 所有権とライフサイクル依存性が異なります。

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 が rooms を作成して所有する
        }
    }
}

aggregationでは、内包されるオブジェクトは独立して存在できます。コンテナはそのオブジェクトを使用しますが、そのライフサイクルを制御しません。TeamとそのPlayerオブジェクトについて考えてみましょう。プレイヤーはチームがなくても存在します。

class Team {
    private List<Player> players;
    
    public Team() {
        players = new ArrayList<>();
    }
    
    public void addPlayer(Player player) {
        players.add(player);  // Teamは既存のプレイヤーを使用する
    }
}

重要な違いは、composition ではコンテナがその部品を内部で作成するという点です。aggregation では、オブジェクトが外部から渡されます。Team が削除されても、Player オブジェクトは存在し続けます。House が削除されると、its Room オブジェクトも同時に失われます。

challenge icon

チャレンジ

簡単

aggregation と composition の両方を示す大学のコースシステムを構築しましょう。Course が自身の Lecture オブジェクトを所有する仕組み(composition)と、独立して存在する Professor オブジェクトを参照するだけの仕組み(aggregation)を確認できます。

コードを4つのファイルに分けて整理します。

  • Professor.java:どのコースからも独立して存在できる教授を表す class を作成します。Professor には2つの private fields、name(String)と department(String)があります。両方の fields を初期化する constructor と、それぞれの getter methods を含めます。toString() を override し、次の値を返します:Prof. [name] ([department])
  • Lecture.java:コース内でのみ意味を持つ講義を表す class を作成します。Lecture には2つの private fields、topic(String)と durationMinutes(int)があります。constructor と getters を含めます。toString() を override し、次の値を返します:Lecture: [topic] ([durationMinutes] min)
  • Course.java:ここで両方の関係を示します。Course には3つの private fields、title(String)、professor(Professor - aggregation)、lectures(Lecture オブジェクトの array - composition)があります。

    constructor は、コースのタイトル、Professor オブジェクト(外部から渡される - aggregation)、および lectures の数を受け取ります。constructor 内で Lecture array を作成し、新しい Lecture オブジェクトで満たします。index i の各 lecture について、topic を "Topic " + (i + 1)、duration を 45 minutes として作成します。この内部での作成が composition を示します。

    複数行の String を返す getInfo() method を追加します。

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

    さらに、各 lecture の toString() をそれぞれ独自の行に含む String を返す listLectures() method も追加します。

  • Main.java:すべてをまとめます。4つの inputs、つまり professor name(String)、professor department(String)、course title(String)、number of lectures(int)を受け取ります。

    まず、指定された name と department を使って Professor を作成します。この Professor は独立して存在します。次に、title、Professor(aggregation)、lecture count(Course が自身の lectures を内部で作成します - composition)を渡して Course を作成します。

    getInfo() の結果を出力し、次に empty line を出力し、その後 listLectures() の結果を出力します。

4つの inputs を、professor name、department、course title、number of lectures の順に受け取ります。

重要な違いに注目してください。Professor は外部で作成され、Course に渡されます(aggregation:その Professor は他の courses を教えたり、この Course がなくても存在したりできます)。一方、Lectures は Course constructor 内で作成されます(composition)。Lectures はこの Course 専用であり、この 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: Courseオブジェクトを作成し、professorを渡す(集約)
        // コースは内部で独自のlecturesを作成する(コンポジション)
        
        // TODO: getInfo()の結果を出力する
        
        // TODO: 空行を出力する
        
        // TODO: listLectures()の結果を出力する
        
    }
}
quiz icon腕試し

このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。

オブジェクト指向プログラミングのすべてのレッスン

自分で練習してみよう: Javaオンラインコンパイラ