الـ Aggregation مقابل الـ Composition
جزء من قسم Object Oriented Programming في رحلة Java على Coddy — الدرس 48 من 87.
يعد كل من التجميع (aggregation) والتركيب (composition) شكلين من علاقات "has-a"، ولكنهما يختلفان في جانب جوهري واحد: الملكية والتبعية في دورة الحياة.
في التركيب (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(); // المنزل ينشئ الغرف ويمتلكها
}
}
}في عملية التجميع (aggregation)، يمكن للكائن المضمن أن يوجد بشكل مستقل. تستخدم الحاوية الكائن ولكنها لا تتحكم في دورة حياته. فكر في Team وكائناته من نوع Player—اللاعبون موجودون حتى بدون فريق:
class Team {
private List<Player> players;
public Team() {
players = new ArrayList<>();
}
public void addPlayer(Player player) {
players.add(player); // الفريق يستخدم اللاعبين الحاليين
}
}التمييز الأساسي: في التكوين (composition)، تقوم الحاوية بإنشاء أجزائها داخلياً. أما في التجميع (aggregation)، فيتم تمرير الكائنات من الخارج. عندما يتم حذف Team، تظل كائنات Player موجودة. وعندما يتم حذف House، تختفي كائنات Room الخاصة به أيضاً.
التحدي
سهللنقم ببناء نظام دورات جامعية يوضح كلاً من التجميع (aggregation) والتركيب (composition). سترى كيف تمتلك Course كائنات Lecture الخاصة بها (تركيب)، بينما تشير فقط إلى كائنات Professor التي توجد بشكل مستقل (تجميع).
ستقوم بتنظيم الكود الخاص بك عبر أربعة ملفات:
Professor.java: قم بإنشاء فئة تمثل أستاذاً يمكنه التواجد بشكل مستقل عن أي دورة تدريبية. لدى Professor حقلان خاصان:name(String) وdepartment(String). قم بتضمين منشئ (constructor) لتهيئة كلا الحقلين وطرق getter لكل منهما. قم بإعادة كتابةtoString()لتعيد:Prof. [name] ([department])Lecture.java: قم بإنشاء فئة تمثل محاضرة لا يكون لها معنى إلا داخل دورة تدريبية. لدى Lecture حقلان خاصان:topic(String) وdurationMinutes(int). قم بتضمين منشئ وطرق getter. قم بإعادة كتابةtoString()لتعيد:Lecture: [topic] ([durationMinutes] min)Course.java: هذا هو المكان الذي ستوضح فيه كلتا العلاقتين! لدى Course ثلاثة حقول خاصة:title(String)، وprofessor(Professor - تجميع)، وlectures(مصفوفة من كائنات Lecture - تركيب).يأخذ المنشئ عنوان الدورة، وكائن Professor (يتم تمريره من الخارج - تجميع)، وعدد المحاضرات. داخل المنشئ، قم بإنشاء مصفوفة Lecture واملأها بكائنات Lecture جديدة. لكل محاضرة عند الفهرس
i، قم بإنشائها بموضوع"Topic " + (i + 1)ومدة45دقيقة. يوضح هذا الإنشاء الداخلي مفهوم التركيب (composition).أضف طريقة
getInfo()التي تعيد سلسلة نصية (String) متعددة الأسطر:Course: [title] Instructor: [professor.toString()] Lectures: [number of lectures]أضف أيضاً طريقة
listLectures()التي تعيد سلسلة نصية معtoString()الخاصة بكل محاضرة في سطر مستقل.Main.java: اجمع كل شيء معاً! ستتلقى أربعة مدخلات: اسم الأستاذ (String)، قسم الأستاذ (String)، عنوان الدورة (String)، وعدد المحاضرات (int).أولاً، قم بإنشاء Professor بالاسم والقسم المعطيين—هذا الأستاذ موجود بشكل مستقل. ثم قم بإنشاء Course، مع تمرير العنوان، والأستاذ (تجميع)، وعدد المحاضرات (ستقوم الدورة بإنشاء محاضراتها الخاصة داخلياً - تركيب).
اطبع نتيجة
getInfo()، ثم اطبع سطراً فارغاً، ثم اطبع نتيجةlistLectures().
ستتلقى أربعة مدخلات بالترتيب: اسم الأستاذ، القسم، عنوان الدورة، وعدد المحاضرات.
لاحظ الفرق الجوهري: يتم إنشاء Professor في الخارج وتمريره إلى Course (تجميع—يمكن للأستاذ تدريس دورات أخرى أو التواجد بدون هذه الدورة). يتم إنشاء Lectures داخل منشئ Course (تركيب—فهي تنتمي حصرياً لهذه الدورة ولن توجد بدونها).
ورقة مرجعية
كلاً من التجميع (aggregation) و التكوين (composition) هما علاقات من نوع "has-a" تختلف في الملكية والتبعية في دورة الحياة.
التكوين (Composition): لا يمكن للكائن المحتوى أن يوجد بشكل مستقل عن الحاوية. عندما يتم تدمير الحاوية، يتم تدمير أجزائها أيضاً. تقوم الحاوية بإنشاء أجزائها داخلياً.
class House {
private Room[] rooms;
public House(int numRooms) {
rooms = new Room[numRooms];
for (int i = 0; i < numRooms; i++) {
rooms[i] = new Room(); // المنزل ينشئ الغرف ويمتلكها
}
}
}التجميع (Aggregation): يمكن للكائن المحتوى أن يوجد بشكل مستقل. تستخدم الحاوية الكائن ولكنها لا تتحكم في دورة حياته. يتم تمرير الكائنات من الخارج.
class Team {
private List<Player> players;
public Team() {
players = new ArrayList<>();
}
public void addPlayer(Player player) {
players.add(player); // الفريق يستخدم لاعبين موجودين مسبقاً
}
}الفرق الجوهري: عند حذف الحاوية، تظل الكائنات المجمعة (aggregated) موجودة، بينما يتم تدمير الكائنات المكونة (composed).
جرّب بنفسك
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 (موجود بشكل مستقل - تجميع/aggregation)
// TODO: إنشاء كائن Course، مع تمرير الأستاذ (تجميع/aggregation)
// سيقوم الكورس بإنشاء المحاضرات الخاصة به داخلياً (تكوين/composition)
// TODO: طباعة نتيجة getInfo()
// TODO: طباعة سطر فارغ
// TODO: طباعة نتيجة listLectures()
}
}يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.
جميع دروس Object Oriented Programming
1أساسيات الـ OOP
الملفات الخارجيةمقدمة في الـ OOPالفئات (Classes) مقابل الكائنات (Objects)الكلمة المفتاحية thisالدوال (Methods)الحقول (Attributes)دالة البناء (Constructor)تعدد أشكال دالة البناء (Constructor Overloading)مراجعة - آلة حاسبة بسيطة4الوراثة
الوراثة الأساسية (extends)الكلمة المفتاحية superإعادة تعريف الدوال (@Override)تسلسل المنشئات (Constructor Chaining)كلاس Objectالوراثة الأحادية ومتعددة المستوياتلماذا لا توجد وراثة متعددة للكلاساتمراجعة - هيكلية الموظفين7الدوال الخاصة وفئة Object
دالة toString()equals() و hashCode()دالة clone()compareTo() و Comparableواجهة Comparatorمراجعة - الترتيب المخصص2محددات الوصول والتغليف
نظرة عامة على مستويات الوصولدوال Getter و Setterإخفاء البياناتالكلمة المفتاحية finalمراجعة - مدير الحساب البنكي5تعدد الأشكال (Polymorphism)
أساسيات الـ Method Overloadingالـ Method Overriding (في وقت التشغيل)الـ Upcasting والـ Downcastingمعامل الـ instanceofالفئات والدوال المجردة (Abstract)مراجعة - حاسبة الأشكال8مفاهيم OOP المتقدمة
الـ Composition مقابل الـ Inheritanceالـ Aggregation مقابل الـ Compositionالفئات الداخلية والمتداخلة والمجهولة (Inner, Nested & Anonymous)الـ Enums ودوالهاالـ Records (Java 16+)الـ Sealed Classes (Java 17+)11أنماط التصميم - الجزء الأول
مقدمة في أنماط التصميمنمط Singletonنمط Factoryنمط Builderنمط Observerنمط Strategy3خصائص الفئة والعضو الساكن
المتغيرات الخاصة بالكائن مقابل المتغيرات الساكنةالدوال الساكنةالكتل البرمجية الساكنةالثوابت (static final)ملخص - العداد والأدوات المساعدة6الواجهات (Interfaces) والأصناف المجردة (Abstract Classes)
مقدمة في الواجهات (Interfaces)تنفيذ الواجهات (Interfaces)تنفيذ واجهات متعددةالـ Default و Static في الواجهاتالأصناف المجردة مقابل الواجهاتالواجهات الوظيفية (Functional Interfaces)مراجعة - نظام الدفع9الأنواع العامة (Generics)
مقدمة في الأنواع العامةالأصناف العامة (Generic Classes)الدوال العامة (Generic Methods)معاملات الأنواع المقيدةالرموز البديلة (?, extends, super)مراجعة - الحاويات العامة12أنماط التصميم - الجزء الثاني
نمط الأمرنمط المحولنمط المزيننمط طريقة القالبنمط الحالةنمط التركيبنمط المكرر