Javaでの構成、集約、および関連付け
1. 序章
オブジェクトは、実生活とプログラミングの両方で、オブジェクト間に関係があります。 これらの関係を理解または実装することが難しい場合があります。
このチュートリアルでは、Javaが、構成、集約、関連付けという3つの簡単に混同されるタイプの関係を取り上げることに焦点を当てます。
2. 構成
Composition は、「所属する」タイプの関係です。 これは、オブジェクトの1つが論理的に大きな構造であり、他のオブジェクトが含まれていることを意味します。 つまり、他のオブジェクトの一部またはメンバーです。
あるいは、私たちはしばしばそれを「has-a」関係と呼びます(継承である「is-a」関係とは対照的です)。
たとえば、部屋は建物に属します。つまり、建物には部屋があります。 つまり、基本的に、それを「所属する」と呼ぶか「持つ」と呼ぶかは、視点の問題にすぎません。
コンポジションは、包含オブジェクトがそれを所有しているため、強い種類の「has-a」関係です。 したがって、
含まれているオブジェクトは、そのパーツなしでは存在できないという意味ではないことに注意してください。 たとえば、建物内のすべての壁を壊して、部屋を破壊することができます。 しかし、建物はまだ存在します。
カーディナリティに関しては、包含オブジェクトは必要な数のパーツを持つことができます。 ただし、すべてのパーツには1つのコンテナが必要です。
2.1. UML
UMLでは、構成を次の記号で示します。
ひし形は包含オブジェクトにあり、矢印ではなく線の基部であることに注意してください。 わかりやすくするために、矢印もよく描画します。
したがって、このUML構造をBuilding-Roomの例に使用できます。
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」関係です。 それを作曲と区別するのは、それが所有を伴わないということです。 その結果、オブジェクトのライフサイクルは結び付けられません。オブジェクトのすべてが互いに独立して存在することができます。
たとえば、車とその車輪。 ホイールを外しても、まだ存在します。他の(既存の)ホイールを取り付けたり、別の車に取り付けたりすると、すべてが正常に機能します。
もちろん、車輪のない車や車輪が外れている車は、車輪が付いている車ほど便利ではありません。 しかし、そもそもこの関係が存在したのはそのためです。toは、パーツよりも多くのことができる、より大きな構造にパーツを組み立てます。
集約には所有が含まれないため、メンバーを1つのコンテナのみに関連付ける必要はありません。 たとえば、三角形はセグメントで構成されています。 ただし、三角形は辺としてセグメントを共有できます。
3.1. UML
集約は構成と非常によく似ています。 唯一の論理的な違いは、集約が弱い関係であるということです。
したがって、UML表現も非常に似ています。 唯一の違いは、ダイヤモンドが空であることです。
車とホイールの場合は、次のようにします。
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では、関連付けを矢印でマークできます。
関連付けが双方向の場合、2つの矢印、両端に矢印のある矢印、または矢印のない線を使用できます。
UMLで母親と子供を表すと、次のようになります。
4.2. ソースコード
Javaでは、集約と同じ方法で関連付けをモデル化できます。
class Child {}
class Mother {
List<Child> children;
}
ちょっと待って、
まあ、できません。違いは論理的です。つまり、オブジェクトの1つが他のオブジェクトの一部であるかどうかです。
また、集計で行ったように、両端で手動で参照を維持する必要があります。
class Child {
Mother mother;
}
class Mother {
List<Child> children;
}
5. UMLサイドノート
わかりやすくするために、UML図で関係のカーディナリティを定義したい場合があります。 これを行うには、矢印の端に書き込みます。
関係がないことを意味するため、カーディナリティとしてゼロを書き込むことは意味がないことに注意してください。 唯一の例外は、範囲を使用してオプションの関係を示す場合です。
また、構成には所有者が1人だけいるため、図にはその所有者を示していないことに注意してください。
6. 複雑な例
(少し)もっと複雑な例を見てみましょう!
学部がある大学をモデル化します。 教授は各学科で働いており、お互いに友達がいます。
大学を閉鎖した後、学部は存在しますか? もちろんそうではないので、それは構成です。
しかし、教授はまだ存在します(うまくいけば)。 どちらがより論理的であるかを決定する必要があります。教授を部門の一部と見なすかどうかです。 あるいは:彼らは部門のメンバーですか? はい、そうです。 したがって、それは集合体です。 その上、教授は複数の部門で働くことができます。
教授が別の教授の一部であると言っても意味がないため、教授間の関係は関連性です。
その結果、次のUML図を使用してこの例をモデル化できます。
そして、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でこれらの関係をモデル化する方法も確認しました。
いつものように、例はGitHubでから入手できます。