3分間プログラミング講座 第8回: クラスは「チーム」で働く!~オブジェクト間の連携(関連・集約・コンポジション)~
登場人物:
チャット先生(通称:先生): オブジェクト指向マスター。普段は優しく、たまにユーモアも交えながら解説。
ボット助手(通称:ボット君): プログラミングを始めたばかりの若手。素朴な疑問を投げかけるのが得意。
ボット君: 先生!前回のポリモーフィズムで、オブジェクト指向の三大要素は全部理解できました!でも、クラスって一つ一つ独立して動くわけじゃないですよね?どうやってお互いに協力し合うんですか?
先生: 素晴らしい質問だ、ボット君!まさにそこが今日のテーマ「オブジェクト間の連携」の核心だよ。これまでは個々のクラスやオブジェクトに注目してきたけど、実際のプログラムでは、たくさんのオブジェクトが「チーム」のように協力し合って、大きな機能を実現しているんだ。
ボット君: チームですか!なんだか面白そうです!
先生: そうだね。オブジェクト指向では、クラス間の関係性を適切に設計することが、良いプログラムを作る上で非常に重要なんだ。今日学ぶ「関連」「集約」「コンポジション」は、UML(統一モデリング言語)で定義されているオブジェクト間の関係性を表す概念でもある。これらはクラス図において、クラス間の線と記号で視覚的に表現されるんだ。プログラムの設計段階で、これらの関係性を明確にすることが重要だよ。
「Is-a」と「Has-a」~クラスの関係性の基本~
先生: まず、クラス間の関係性には大きく分けて二つの基本形があることを思い出してほしい。前回学んだ「継承」は、その一つだったね。
ボット君: はい!「Dog is an Animal(犬は動物の一種である)」っていう「is-a」関係ですね!
先生: その通り!「is-a」関係は、継承によって表現される関係性で、「AはBの一種である」という意味を持つ。これは「親クラスの性質を受け継ぐ」関係だ。
先生: 今日学ぶのは、もう一つの重要な関係性、「Has-a」関係だ。これは「AはBを持っている」という意味の関係性で、あるクラスが別のクラスのオブジェクトを「部品」として持っている場合に使うんだ。
ボット君: 持っている?どういうことですか?
先生: 例えば、「車はエンジンを持っている(Car has an Engine)」という関係を考えてみてごらん。車はエンジンの一種ではないよね?でも、エンジンがなければ車は動かない。つまり、車はエンジンという「部品」を持っているんだ。
ボット君: なるほど!車の中にエンジンがある、みたいなイメージですね!
先生: その通り!この「Has-a」関係は、オブジェクト指向では「関連(Association)」として表現されることが多い。そして、その関連の中でも、特に密接な関係を持つものを「集約(Aggregation)」や「コンポジション(Composition)」と呼ぶんだ。
オブジェクト間の連携の種類
先生: 「Has-a」関係を表現する方法には、主に以下の3つのレベルがあるよ。
1. 関連(Association)
先生: 最も一般的な「Has-a」の関係が「関連(Association)」だ。これは、二つのクラスが互いに独立して存在できるけれど、何らかの形で協力し合う必要がある場合に使う。
ボット君: 独立しているけど協力し合う…?
先生: 例えば、「学生は授業を履修する(Student has a Course)」という関係。学生も授業も、それぞれ単独で存在できるよね。学生がいなくても授業は存在するし、授業がなくても学生は存在する。でも、学生が授業を受けるためには、両者が「関連」している必要がある。
Java
// 関連の例: 学生と授業
class Course {
private String title;
public Course(String title) {
this.title = title;
}
public String getTitle() {
return title;
}
}
class Student {
private String name;
// StudentはCourseのオブジェクトを参照する(関連)
private Course enrolledCourse;
public Student(String name) {
this.name = name;
}
public void enrollCourse(Course course) {
this.enrolledCourse = course;
System.out.println(name + "は" + course.getTitle() + "を履修しました。");
}
public void study() {
if (enrolledCourse != null) {
System.out.println(name + "は" + enrolledCourse.getTitle() + "を勉強しています。");
} else {
System.out.println(name + "は履修中の授業がありません。");
}
}
}
public class AssociationExample {
public static void main(String[] args) {
Course math = new Course("数学"); // 授業オブジェクトは独立して生成
Student alice = new Student("アリス");
alice.enrollCourse(math); // アリスが数学を履修
alice.study();
// 授業(math)も学生(alice)も独立して存在できる
// aliceが卒業してもmathは存在し続ける
System.out.println("\nアリスが卒業しても、数学の授業は存在し続ける: " + math.getTitle());
}
}
先生: この例では、StudentクラスがCourseクラスのオブジェクトを属性として持っていることで、両者が関連していることを示している。CourseオブジェクトはStudentが作られるより前に生成され、Studentが消滅してもCourseは独立して存在し続けることができるんだ。
2. 集約(Aggregation)
先生: 次に「集約(Aggregation)」だ。これは「全体と部分」の関係を表すけれど、部分が全体から独立して存在できる場合に使われる。
ボット君: 全体と部分…?さっきの車とエンジンとどう違うんですか?
先生: 良い質問だね!例えば、「大学は複数の学部を持つ(University has Departments)」という関係を考えてみよう。大学がなくなっても、学部(例えば文学部や経済学部)は独立した組織として存続できる可能性があるよね?別の大学に移管されたり、独立した研究機関として存続したりする場合があるんだ。これが集約の「弱い所有関係」を表しているんだ。
ボット君: なるほど!大学がなくなっても、学部自体は残る可能性があるんですね!
先生: そう。つまり、全体(大学)が消滅しても、部分(学部)は生き残れる関係だ。これが集約の特徴だね。
Java
// 集約の例: 大学と学部(部分が全体から独立して存在可能)
import java.util.ArrayList;
import java.util.List;
class Department {
private String name;
public Department(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
class University {
private String name;
// Universityは複数のDepartmentを持つ(集約)
private List<Department> departments;
public University(String name) {
this.name = name;
this.departments = new ArrayList<>();
}
// 既存のDepartmentオブジェクトを受け取る(外部で生成済み)
public void addDepartment(Department department) {
departments.add(department);
System.out.println(name + "に" + department.getName() + "が追加されました。");
}
public void showDepartments() {
System.out.print(name + "の学部: ");
for (Department dept : departments) {
System.out.print(dept.getName() + " ");
}
System.out.println();
}
// 大学が閉鎖されても、学部は独立して存在し続けることができることを示すメソッド
public List<Department> releaseDepartments() {
System.out.println(this.name + "が閉鎖されます。学部を解放します。");
List<Department> releasedDepartments = new ArrayList<>(departments);
departments.clear(); // 大学からは学部の参照をなくす
return releasedDepartments; // 解放された学部リストを返す
}
}
public class AggregationExample {
public static void main(String[] args) {
Department litDept = new Department("文学部"); // 学部オブジェクトは独立して生成
Department econDept = new Department("経済学部");
University myUni = new University("私の大学");
myUni.addDepartment(litDept);
myUni.addDepartment(econDept);
myUni.showDepartments();
// 大学(myUni)が閉鎖される
List<Department> released = myUni.releaseDepartments();
// myUniは消滅しても、litDeptやeconDeptは独立して存在しうる
// 例えば、他の大学で再利用される可能性もある
System.out.println("\n解放された学部:");
for (Department dept : released) {
System.out.println("- " + dept.getName());
}
System.out.println("文学部はまだ存続している: " + litDept.getName());
}
}
先生: UniversityクラスがDepartmentオブジェクトのリストを持っているけれど、DepartmentオブジェクトはUniversityとは別に生成され、Universityが消滅してもDepartment自体は存在し続けることができることを、releaseDepartmentsメソッドで示しているんだ。
3. コンポジション(Composition)
先生: そして最後に「コンポジション(Composition)」だ。これも「全体と部分」の関係を表すけれど、部分が全体から独立して存在できない、つまり全体が消滅したら部分も一緒に消滅する場合に使われる。
ボット君: あ!これはまさに車とエンジンの関係ですね!車が壊れたらエンジンも一緒に終わり、みたいな!
先生: その通り!「人間は心臓を持つ(Human has a Heart)」という関係もそうだね。人間がいなくなれば、心臓も単独では機能し続けられない。つまり、全体オブジェクトが削除されると、部分オブジェクトも同時に削除される「強い所有関係」があることを意味するんだ。
Java
// コンポジションの例: 人間と心臓
class Heart {
public Heart() {
System.out.println("心臓が作られました。");
}
public void beat() {
System.out.println("ドクン、ドクン...");
}
}
class Human {
private String name;
// HumanはHeartを持つ(コンポジション)
private Heart heart; // Humanが生成されるときにHeartも生成される
public Human(String name) {
this.name = name;
this.heart = new Heart(); // Humanの内部でHeartを生成
System.out.println(name + "が生まれました。");
}
public void live() {
System.out.println(name + "は生きています。");
heart.beat();
}
// Humanが消滅すると、その心臓も一緒に消滅する(部分のライフサイクルが全体に完全に依存)
// 例えば、Humanオブジェクトがメモリから解放される(ガベージコレクションされる)と、
// それが持っていたHeartオブジェクトも一緒に参照されなくなり、解放の対象となる。
}
public class CompositionExample {
public static void main(String[] args) {
Human bob = new Human("ボブ");
bob.live();
System.out.println("\nボブがいなくなると、心臓も機能しなくなる...");
// bobオブジェクトへの参照が失われると、Heartオブジェクトも単独では意味をなさなくなる
bob = null; // bobオブジェクトへの参照をなくす
// この時点で、Heartオブジェクトも外部からアクセスできなくなり、メモリ解放の対象となる
}
}
先生: Humanクラスのコンストラクタ内でHeartオブジェクトを生成しているのがポイントだ。このように、部分オブジェクトが全体オブジェクトのライフサイクルに完全に依存している場合にコンポジションを使うんだ。
連携のメリットと使い分け
先生: オブジェクト間の連携を適切に使うことで、どんなメリットがあると思う?
ボット君: うーん、クラスの役割がもっとはっきりするような気がします!あと、プログラムがもっと現実の世界に近づくような…?
先生: その通り!主なメリットは以下の通りだ。
現実世界のモデリング: 現実世界の複雑な関係性(「~は~を持っている」)をプログラム上で自然に表現できる。
責任の明確化: 各クラスが自分の責任範囲に集中し、他のクラスのオブジェクトを「部品」として利用することで、役割が明確になる。
コードの再利用性: 独立した部品クラス(例:Engineクラス)を、様々な全体クラス(例:Car、Boat)で再利用できる。
柔軟性と拡張性: 部品を差し替えたり、新しい部品を追加したりすることで、全体の機能を容易に変更・拡張できる。
先生: そして、関連、集約、コンポジションの使い分けは、その関係性の「依存度」と「ライフサイクル」を基準に考えるのが良い。
関連: 最も緩やかな関係。互いに独立して存在し、一時的な繋がりも含む。部分が全体から独立して生成・消滅する。
集約: 全体と部分の関係だが、部分が全体から独立して存在できる。全体が消滅しても、部分が存続しうる。「弱い所有関係」を表す。
コンポジション: 全体と部分の関係で、部分が全体に強く依存し、全体が消滅すれば部分も一緒に消滅する。部分が全体に内包され、ライフサイクルが一致する「強い所有関係」を表す。
ボット君: なるほど!ただ「持っている」だけじゃなくて、その「持ち方」にも種類があるんですね!奥が深いなあ!
先生: その通り!これらの関係性を理解し、適切に使いこなすことが、大規模で複雑なシステムを設計する上で非常に重要になるんだ。
今日のまとめ!
クラスの関係性:
Is-a関係: 継承(extends)で表現。「AはBの一種である」。
Has-a関係: オブジェクト間の連携で表現。「AはBを持っている」。
Has-a関係の主な種類:
関連(Association): 最も一般的な関係。互いに独立して存在し、参照し合う。
集約(Aggregation): 全体と部分の関係だが、部分が全体から独立して存在できる(例: 大学と学部)。「弱い所有関係」。
コンポジション(Composition): 全体と部分の関係で、部分が全体に強く依存し、全体が消滅すれば部分も消滅する(例: 人間と心臓)。「強い所有関係」。
メリット: 現実世界のモデリング、責任の明確化、コードの再利用性、柔軟性と拡張性。
使い分け: 関係性の「依存度」と「ライフサイクル」を基準に判断する。
ボット君: 先生、今日はオブジェクト間の「チームワーク」について、すごくよく分かりました!ただクラスを作るだけじゃなくて、どう繋げるかが大事なんですね!
先生: その調子だ、ボット君!オブジェクト指向は、まさに「部品(オブジェクト)」を組み合わせて「システム」を作り上げる考え方だからね。次回は、この「部品」を作る上で、もっと柔軟で拡張性の高い設計をするための「約束事」について学んでいこう。それが「インターフェース」と「抽象クラス」だ。
ボット君: 約束事!なんだかプロの技って感じがします!楽しみです!ありがとうございました!
次回:第9回:設計の約束事~インターフェースと抽象クラス~
#オブジェクト指向 #プログラミング初心者 #Java #クラスとメソッド #プログラミング学習 #入門
いいなと思ったら応援しよう!
もしこの記事が少しでも役に立ったり、楽しんでいただけたりしたら、チップをいただけるととても嬉しいです!
いただいたチップは、今後のより良いコンテンツ制作のための書籍代📚とnote購入🗒️、他の方へのチップ代💰に使わせていただきます。
いつも応援ありがとうございます!