Javaでの構成、集約、および関連付け

  • link:/category/architecture/ [アーキテクチャ]

  • Java

1. 前書き

オブジェクトは、実生活とプログラミングの両方で、オブジェクト間に関係があります。 これらの関係を理解または実装することが難しい場合があります。
このチュートリアルでは、構成、集約、および関連付けという3つのタイプの関係を簡単に混同するJavaに焦点を当てます。

2. 組成

_Composition_は、 "belongs-to"タイプの関係です。 これは、オブジェクトの1つが論理的に大きな構造であり、他のオブジェクトが含まれていることを意味します。 つまり、他のオブジェクトの一部またはメンバーです。
または、「link:/java-inheritance[inheritance]である「is-a」関係とは対照的に、しばしば「has-a」関係*と呼びます。
たとえば、部屋は建物に属します。つまり、建物には部屋があります。 したがって、基本的に、「belongs-to」または「has-a」と呼ぶかどうかは視点の問題にすぎません。
構成は、包含オブジェクトが所有しているため、強い種類の「has-a」関係です。 したがって、*オブジェクトのライフサイクルは結びついています。 つまり、所有者オブジェクトを破壊すると、そのメンバーも破壊されます。*たとえば、前の例では、建物とともに部屋が破壊されます。
これは、包含オブジェクトがその部分なしでは存在できないという意味ではないことに注意してください。 たとえば、建物内のすべての壁を壊し、部屋を破壊することができます。 しかし、建物はまだ存在します。
カーディナリティの観点から、包含オブジェクトには必要な数のパーツを含めることができます。 ただし、*すべてのパーツにはコンテナが1つだけ必要です*。

2.1. UML

UMLでは、次の記号で構成を示します。
link:/uploads/composition-100x13.png%20100w []
菱形は包含オブジェクトにあり、矢じりではなく線の基部であることに注意してください。 明確にするために、しばしば矢印も描画します。
link:/uploads/composition-arrow-100x13.png%20100w []
そのため、Building-Roomの例にこのUMLコンストラクトを使用できます。
link:/uploads/composition-example-100x24.png%20100w []

2.2. ソースコード

Javaでは、非静的内部クラスを使用してこれをモデル化できます。
class Building {
    class Room {}
}
または、そのクラスをメソッド本体で宣言することもできます。 名前付きクラス、匿名クラス、ラムダのいずれでもかまいません。
class Building {
    Room createAnonymousRoom() {
        return new Room() {
            @Override
            void doInRoom() {}
        };
    }

    Room createInlineRoom() {
        class InlineRoom implements Room {
            @Override
            void doInRoom() {}
        }
        return new InlineRoom();
    }

    Room createLambdaRoom() {
        return () -> {};
    }

    interface Room {
        void doInRoom();
    }
}
すべてのインスタンスを包含クラスにバインドするため、内部クラスは非静的でなければならないことに注意してください。
通常、包含オブジェクトはそのメンバーにアクセスしたいと考えています。 したがって、参照を保存する必要があります。
class Building {
    List<Room> rooms;
    class Room {}
}
すべての内部クラスオブジェクトには、それらを含むオブジェクトへの暗黙的な参照が格納されていることに注意してください。 そのため、アクセスするために手動で保存する必要はありません。
class Building {
    String address;

    class Room {
        String getBuildingAddress() {
            return Building.this.address;
        }
    }
}

3. 集約

集約も「has-a」関係です。 それを作曲と区別するもの、所有することを含まないこと。 その結果、オブジェクトのライフサイクルは結び付けられていません。オブジェクトのすべてが互いに独立して存在できます。
たとえば、車とその車輪。 *私たちは車輪を脱ぐことができ、それらはまだ存在します。*私たちは他の(既存の)車輪を取り付けるか、またはこれらを別の車にインストールすることができ、すべてがうまく動作します。
もちろん、車輪のない車や取り外した車輪は、車輪が付いている車ほど有用ではありません。 しかし、それがこの関係がそもそも存在していた理由です:*パーツをより大きなコンストラクトにアセンブルし、パーツよりも多くのことができる*。
集約には所有が含まれないため、*メンバーは1つのコンテナにのみ結び付けられる必要はありません*。 たとえば、三角形はセグメントで構成されています。 ただし、三角形は辺としてセグメントを共有できます。

3.1. UML

集約は構成に非常に似ています。 唯一の論理的な違いは、集約が弱い関係であることです。
したがって、UML表現も非常に似ています。 唯一の違いは、ダイヤモンドが空であることです。
link:/uploads/aggregation-100x13.png%20100w []
車とホイールの場合、次のようにします。
link:/uploads/aggregation-example-100x24.png%20100w []

3.2. ソースコード

Javaでは、単純な古い参照を使用して集約をモデル化できます。
class Wheel {}

class Car {
    List<Wheel> wheels;
}
メンバーは、非静的内部クラスを除く、任意のタイプのクラスにすることができます。
上記のコードスニペットでは、両方のクラスに個別のソースファイルがあります。 ただし、静的な内部クラスを使用することもできます。
class Car {
    List<Wheel> wheels;
    static class Wheel {}
}
Javaは、静的でない内部クラスでのみ暗黙的な参照を作成することに注意してください。 そのため、必要な場所で手動で関係を維持する必要があります。
class Wheel {
    Car car;
}

class Car {
    List<Wheel> wheels;
}

4. 協会

関連付けは、3つの間の最も弱い関係です。 *「has-a」関係ではありません*。オブジェクトはいずれも、別のオブジェクトの一部またはメンバーではありません。
*関連付けとは、オブジェクトが互いに「知っている」ことだけを意味します。*たとえば、母親と子供。

4.1. UML

UMLでは、関連付けを矢印でマークできます。
link:/uploads/association-100x13.png%20100w []
関連付けが双方向の場合、2つの矢印、両端に矢印の付いた矢印、または矢印のない線を使用できます。
link:/uploads/association-bidirectional-100x67.png%20100w []
UMLで母親と子供を表すことができます:
link:/uploads/association-example-100x13.png%20100w []

4.2. ソースコード

Javaでは、集約と同じ方法で関連付けをモデル化できます。
class Child {}

class Mother {
    List<Child> children;
}
しかし、*参照が集約または関連付けを意味するかどうかはどうすればわかりますか?*
*まあ、私たちはできません。*違いは論理的です:オブジェクトの1つが他のオブジェクトの一部であるかどうか。
また、集計で行ったように、両端で参照を手動で維持する必要があります。
class Child {
    Mother mother;
}

class Mother {
    List<Child> children;
}

5. UMLサイドノート

明確にするために、UMLダイアグラム上の関係のカーディナリティを定義することがあります。 これを行うには、矢印の端に書き込みます。
link:/uploads/cardinality-1-100x24.png%20100w []
関係がないことを意味するため、カーディナリティとしてゼロを書き込むことは意味がないことに注意してください。 唯一の例外は、オプションの関係を示すために範囲を使用する場合です。
link:/uploads/cardinality-2-100x15.png%20100w []
また、作曲には1人の所有者しかいないため、図には表示されないことに注意してください。

6. 複雑な例

(少し)より複雑な例を見てみましょう!
学部がある大学をモデル化します。 教授は各学部で働いており、各学部には友人もいます。
大学を閉鎖した後、学科は存在しますか? もちろんそうではないので、それは作曲です。
しかし、教授はまだ存在します(うまくいけば)。 どちらがより論理的であるかを決定する必要があります。教授を学科の一部とみなすかどうか。 あるいは、彼らは部門のメンバーかどうか? はい、そうです。 したがって、それは集約です。 その上、教授は複数の部門で働くことができます。
教授が他の教授の一部であると言うのは意味をなさないため、教授間の関係は連想です。
その結果、次のUMLダイアグラムを使用してこの例をモデリングできます。
link:/uploads/complex-example-100x237.png%20100w []
そして、Javaコードは次のようになります。
class University {
    List<Department> department;
}

class Department {
    List<Professor> professors;
}

class Professor {
    List<Department> department;
    List<Professor> friends;
}
「has-a」、「belongs-to」、「member-of」、「part-of」などの用語に依存する場合、オブジェクト間の関係をより簡単に識別できることに注意してください。

7. 結論

この記事では、構成、集計、および関連付けのプロパティと表現について説明しました。 また、これらの関係をUMLおよびJavaでモデル化する方法も確認しました。
いつものように、サンプルはhttps://github.com/eugenp/tutorials/tree/master/core-java-modules/core-java-lang-oop-3[GitHubで]から入手できます。