English 🇬🇧
CPCS203  ·  الوحدة 05  ·  الجزء الثاني من ثلاثة

🔗 علاقات الكائنات

الاعتماد والتجميع والتركيب | البرمجة كائنية التوجه — الوحدة الخامسة، الجزء الثاني

🔧 الاعتماد (Dependency) 📦 التجميع (Aggregation) 💎 التركيب (Composition) 🔄 التجميع الذاتي (Self-Aggregation) 🔢 التعددية (Multiplicity)
📋 فهرس المحتويات
  1. علاقات الكائنات — الصورة الكاملة
  2. الاعتماد (Dependency) — "يستخدم"
  3. التجميع والتركيب (Aggregation & Composition) — "يمتلك"
  4. التجميع الذاتي (Self-Aggregation)
  5. التعددية (Multiplicity) — كم عدد الكائنات المشاركة؟
1
علاقات الكائنات — الصورة الكاملة
🏫 الأستاذ يقدّم العلاقات
👨‍🏫
الأستاذ
اليوم سنتعلم شيئًا بالغ الأهمية، يُسمى العلاقات. سأجعلها بسيطة لكم. هناك ثلاث علاقات سنتعلمها. هناك علاقة الاعتماد (Dependency)، وهناك التجميع (Aggregation)، وهناك الوراثة (Inheritance). بسيط جدًا!

الاعتماد يقول "يستخدم." التجميع يقول "يمتلك." الوراثة تقول "هو نوع من."
🔧
الاعتماد (Dependency)
"uses"
الفئة (Class) A تستخدم الفئة B بصورة مؤقتة (كمعامل دالة أو متغير محلي). B ليست جزءًا من هوية (Identity) A — إنها مجرد أداة.
📦
التجميع (Aggregation)
"has"
الفئة A تمتلك فئة B مخزّنة كحقل (Field). يأتي بقوتين: التجميع (Aggregation) (ضعيف) والتركيب (Composition) (قوي).
🧬
الوراثة (Inheritance)
"is"
الفئة A هي نوع من الفئة B. يُدرَّس في الوحدة السادسة.
📌 للتجميع (Aggregation) قوتان

كلٌّ من التجميع (Aggregation) والتركيب (Composition) علاقة "يمتلك" — فكلاهما يعني أن فئة ما تخزّن كائنًا آخر كحقل (Field). الفرق هو مدة حياة الكائن المحتوى.

📦
التجميع (Aggregation)
"has-a" (weak)
الكائن المحتوى يمكنه الوجود بشكل مستقل. يمكن مشاركته بين عدة مالكين. رمز الماسة المفتوحة ◇ في مخطط الفئة (UML Class Diagram).
💎
التركيب (Composition)
"owns-a" (strong)
الكائن المحتوى لا يمكنه الوجود بدون مالكه. يموت حين يموت المالك. رمز الماسة المملوءة ◆ في مخطط الفئة (UML Class Diagram).
🎓 السيناريو العملي — أربع فئات
👨‍🏫
الأستاذ
تخيّل أن لدينا فئة (Class) باسم Student. اقترح الطلاب: "ينبغي أن نقسّم الطالب إلى كائنات أصغر — الطالب لديه Address وName، وكلاهما يمكن أن يكون كائنًا!"
👨‍🏫
الأستاذ
إذن لدينا الآن فئة Address — تحتوي على اسم الشارع والمدينة (مثلاً "جدة") ورقم الوحدة (مثلاً 100). ولدينا فئة Name — تحتوي على الاسم الأول والأوسط والأخير. ولدينا فئة BMICalculator — تمرّر لها الوزن بالكيلوجرام والطول بالسنتيمتر وتحصل على مؤشر كتلة الجسم (BMI). هذا كل شيء!

إذن كم عدد الفئات؟ أربع فئات: Student، Address، Name، BMICalculator. الآن — ما هي العلاقة بينها؟
Student -id : int -name : Name -address : Address -marks : int[] +Student(id: int, first: String, last: String, address: Address) +getId() : int +getFullName() : String +getAddress() : Address +getMarks() : int[] +calculateBMI(calc: BMICalculator) : double Name -firstName : String -lastName : String +Name(first: String, last: String) +getFirstName() : String +getLastName() : String +getFullName() : String Address -street : String -city : String -unit : int +Address(street: String, city: String, unit: int) +getStreet() : String +getCity() : String +getUnit() : int BMICalculator -weight : double -height : double +BMICalculator(weight: double, height: double) +getBMI() : double +getBMI(weight: double, height: double) : double Aggregating class Collects Name, Address as fields. Uses BMICalculator temporarily. Composition ◆ Created inside Student constructor. Cannot exist without a Student. Dies when Student dies. Aggregation ◇ Created outside, passed in. Can be shared by many students. Survives independently. Dependency Passed as a method parameter. NOT stored as a field. Not part of student's identity. Can also be made static. composition ◆ owns 1 1 aggregation ◇ has * 1 «uses» dependency

أربع فئات — ثلاثة أنواع مختلفة من العلاقات مع Student

2
الاعتماد (Dependency) — "يستخدم"

لتحديد نوع العلاقة التي تربط BMICalculator بـStudent، لا نبدأ بالكود — بل نبدأ بالنموذج (Model). نطرح سؤالًا فلسفيًا واحدًا: "هل BMICalculator جزء من هوية (Identity) الطالب؟" الإجابة على هذا السؤال تحدد كل شيء.

🏫 الخطوة الأولى — سؤال الهوية (Identity): ما الذي يُعرّف الطالب؟
👨‍🏫
الأستاذ
دعني أسألكم: ما الذي يُعرّف الطالب؟ ما الأشياء التي تجعل هذا الطالب هو هو؟
🙋
الطلاب
اسمه، ورقم هويته، وعنوانه، ودرجاته!
👨‍🏫
الأستاذ
بالضبط. الاسم، والرقم، والعنوان، والدرجات — هذه هي الأشياء التي تُشكّل هوية (Identity) الطالب. إنها تصف من هو. الآن — هل يُعرّفه BMICalculator؟ هل هو جزء من كينونته؟
🙋
الطلاب
لا… BMICalculator ليس من هو. إنه مجرد شيء يستخدمه.
👨‍🏫
الأستاذ
صحيح! تخيّل الآن أن BMICalculator جهاز — آلة واقفة في ممر الجامعة. يقترب منها الطالب، يُدخل وزنه وطوله، يحصل على قيمة مؤشر كتلة الجسم (BMI)، ثم يمضي. الآلة ليست جزءًا منه. لا يحملها. لا يمتلكها. أي طالب يمكنه استخدامها. إنها لا علاقة لها بهويته (Identity) أبدًا.
🧠 الخطوة الثانية — الفلسفة أولًا، الكود ثانيًا
👨‍🏫
الأستاذ
إذن القرار يُتخذ على مستوى النموذج (Model)، لا على مستوى الكود. لأن BMICalculator ليس جزءًا من هوية (Identity) الطالب، فنحن لا نخزّنه كحقل (Field) في بيانات الطالب. نستخدمه فقط حين نحتاجه — نمرّره كـمعامل دالة (Method Parameter). هذا ما ستراه في الكود. لكن السبب فلسفي — ليس نحويًا.
🙋
الطالب
أستاذ — لكن ماذا لو كتبته كحقل (Field) في البيانات؟ هل سيصبح تجميعًا (Aggregation) حينئذ؟
👨‍🏫
الأستاذ
سؤال ممتاز — والإجابة تعتمد على الواقع. اسأل نفسك: هل يمتلك كل طالب جهاز BMICalculator شخصيًا؟ لو كان كل طالب يحمل جهازه الخاص — الذي ينتمي له وحده دون غيره — لكان منطقيًا تخزينه كحقل (Field)، وستكون العلاقة تجميعًا (Aggregation). لكن انظر ما هو BMICalculator في حقيقته. لا يحتوي على أي بيانات خاصة بطالب. لا ارتباط له بأي طالب بعينه. إنه أداة مشتركة — كالآلة في الممر التي يمكن لأي طالب استخدامها. لا يمتلكها أحد. لا يحملها أحد كجزء من هويته. فلو كتبتها كحقل (Field)، سيُترجَم الكود — لكنك ستكون قد وصفت شيئًا غير حقيقي. الكود المفترض أن يعكس النموذج. إذا كان الكود يتناقض مع النموذج، فالكود خاطئ.
🙋
الطلاب
إذن القاعدة: إذا لم يكن جزءًا من الهوية (Identity) ← لا تجعله حقلًا (Field) ← استخدمه كمعامل (Parameter) ← وهذا هو الاعتماد (Dependency)!
👨‍🏫
الأستاذ
بالضبط! ✅ ولاحظ — يمكننا الذهاب أبعد من ذلك وجعل getBMI() دالة ثابتة (static). لأن BMICalculator لا ارتباط له بأي طالب محدد، لا نحتاج حتى إلى كائن — ننادي مباشرةً BMICalculator.getBMI(weight, height). هذا أكثر الطرق طبيعيةً للتعبير عن كونه أداة مساعدة بحتة، بلا ملكية ولا علاقة بالهوية (Identity).
⚠️ المبدأ الأساسي — النموذج هو الذي يحدد العلاقة، لا الكود
السؤال الذي يجب طرحه الإجابة العلاقة ← النتيجة في الكود
هل هو جزء من هوية (Identity) الطالب؟ نعم (Name، Address) تجميع / تركيب ← خزّنه كحقل (Field)
هل هو جزء من هوية (Identity) الطالب؟ لا (BMICalculator) اعتماد (Dependency) ← استخدمه كمعامل دالة (Method Parameter) (أو اجعله ثابتًا)

اتجاه التفكير: الفلسفة ← النموذج ← الكود. أبدًا بالعكس. إذا صادفت وكتبت BMICalculator كحقل (Field)، فهذا لا يجعله تجميعًا (Aggregation) — بل يجعله خطأً في النمذجة.

Student «uses» BMICalculator

السهم المتقطع المفتوح = الاعتماد (Dependency) — علاقة "يستخدم"

📄 BMICalculator.java — مُستخدَم كاعتماد (Dependency)
public class BMICalculator { private double weight; // بالكيلوجرام private double height; // بالأمتار public BMICalculator(double weight, double height) { this.weight = weight; this.height = height; } public double getBMI() { return weight / (height * height); } } // في فئة Student — الاعتماد (Dependency): BMICalculator معامل دالة (Method Parameter) وليس حقلًا (Field) public double calculateBMI(BMICalculator calc) { return calc.getBMI(); // مُستخدَم بصورة مؤقتة — لا يُخزَّن كحقل (Field)! } // ── أو باستخدام دالة ثابتة (static method) — أكثر نظافةً، لا حاجة لكائن أصلًا ── public class BMICalculator { public static double getBMI(double weight, double height) { return weight / (height * height); } } // في Student: لا كائن يُنشأ، ننادي الحاسبة مباشرةً double bmi = BMICalculator.getBMI(70, 1.75);
3
التجميع والتركيب (Aggregation & Composition) — "يمتلك"

كلٌّ من التجميع (Aggregation) والتركيب (Composition) علاقة "يمتلك" — تخزّن الفئة كائنًا آخر كحقل (Field) في بياناتها. السؤال الجوهري دائمًا: "هل يمكن للكائن المحتوى أن يبقى حيًّا بعد غياب مالكه؟"

🏫 الخطوة الأولى — ماذا يعني "التجميع (Aggregation)"؟
👨‍🏫
الأستاذ
سؤال واحد — ماذا يعني "التجميع (Aggregation)"؟ لدينا مصطلحان يجب فهمهما: الفئة المُجمِّعة (Aggregating class) والفئة المُجمَّعة (Aggregated class).

التجميع (Aggregation) يعني تجميع — جمع أشياء معًا. في حالتنا، فئة Student هي الفئة المُجمِّعة — تجمع كائنات أخرى بداخلها. أما Address وName فهما الفئتان المُجمَّعتان — يُجمَعان داخل الطالب.
🏠 الخطوة الثانية — تشبيه الشقة: التركيب (Composition) مقابل التجميع (Aggregation)
👨‍🏫
الأستاذ
دعني أطرح عليكم سؤالًا بسيطًا جدًا. هل يمكنكم تخيّل اسم (Name) بدون طالب؟ هل يمكننا أن نقول "هذا علي فهد" — لكن أين هو؟ ينتمي لمن؟ لمن هذا الاسم؟ لا يوجد طالب — لا يوجد اسم!
🙋
الطالب
هذا صحيح يا أستاذ. "علي فهد" لا يمكنه الوجود بمعزل عن صاحبه. الاسم لا معنى له إلا إذا كان هناك شخص — طالب!
👨‍🏫
الأستاذ
تخيّل الآن ثلاثة طلاب — علي وخالد وفهد — يسكنون في نفس الشقة في شارع الجامعة، وحدة 100، جدة. كلهم يشتركون في نفس العنوان.

الآن علي يغادر الجامعة. ماذا يحدث لعنوان "شارع الجامعة، وحدة 100، جدة"؟ هل يختفي؟
🙋
الطلاب
لا! العنوان ما زال موجودًا — خالد وفهد ما زالا يسكنان هناك! العنوان بقي حيًّا حتى بعد مغادرة علي.
👨‍🏫
الأستاذ
بالضبط! ✅ لكن لو مات علي — فلا بد أن يموت اسم "علي فهد" معه. هل ترون العلاقة؟

🔵 Address → تجميع (Aggregation) (ضعيف): يمكن مشاركة العنوان وهو يوجد باستقلالية بعيدًا عن أي طالب بعينه.
💚 Name → تركيب (Composition) (قوي): الاسم لا يمكنه الوجود بدون صاحبه — يموت مع الطالب.
🙋
الطلاب
رائع! 😮 وBMICalculator — ليس جزءًا من الطالب أصلًا، أليس كذلك؟ ليس تجميعًا (Aggregation) أيضًا. يُستخدَم فقط بصورة مؤقتة — إنه اعتماد (Dependency)!
👨‍🏫
الأستاذ
ممتاز! حاسبة مؤشر كتلة الجسم ليست سوى أداة تُستخدَم بصورة مؤقتة لحساب قيمة — لا تُسهم في هوية (Identity) الطالب أو تعريفه. هذا بالضبط هو سبب كونها اعتمادًا (Dependency)، لا تجميعًا (Aggregation). 🎉
الجانب 🔵 التجميع (Aggregation) — ضعيف 💚 التركيب (Composition) — قوي
الكلمة المفتاحية "has-a" "owns-a"
مدة حياة الكائن المحتوى يبقى مستقلًا — قابل للمشاركة يموت حين يموت مالكه — حصري
المثال الواقعي Student و Address (3 طلاب، عنوان واحد) Student و Name (الاسم يموت مع الطالب)
رمز مخطط الفئة (UML) ماسة مفتوحة ◇ ماسة مملوءة ◆
Java: أين يُنشأ الكائن؟ يُنشأ خارجيًا — يُمرَّر كمعامل (Parameter) يُنشأ داخل المُنشئ (constructor) بـnew
Student -id : int -name : Name -address : Address -marks : int[] +Student(id: int, first: String, last: String, address: Address) +getId() : int +getFullName() : String +getAddress() : Address +getMarks() : int[] +calculateBMI(calc: BMICalculator) : double Name -firstName : String -lastName : String +Name(first: String, last: String) +getFirstName() : String +getLastName() : String +getFullName() : String Address -street : String -city : String -unit : int +Address(street: String, city: String, unit: int) +getStreet() : String +getCity() : String +getUnit() : int BMICalculator -weight : double -height : double +BMICalculator(weight: double, height: double) +getBMI() : double +getBMI(weight: double, height: double) : double Aggregating class Collects Name, Address as fields. Uses BMICalculator temporarily. Composition ◆ Created inside Student constructor. Cannot exist without a Student. Dies when Student dies. Aggregation ◇ Created outside, passed in. Can be shared by many students. Survives independently. Dependency Passed as a method parameter. NOT stored as a field. Not part of student's identity. Can also be made static. composition ◆ owns 1 1 aggregation ◇ has * 1 «uses» dependency

مخطط الفئة (UML Class Diagram) — Student ◆ Name (تركيب)  |  Student ◇ Address (تجميع)  |  Student - - → BMICalculator (اعتماد)

🏫 الخطوة الثالثة — تحدي الطالب: كيف أكتب هذا بـ Java؟
👨‍🏫
الأستاذ
طلابي — درسنا الآن التجميع (Aggregation) والتركيب (Composition) عن كثب. أنتم تعرفون المثال: Student وName وAddress. وتعرفون أيهما تركيب (Composition) وأيهما تجميع (Aggregation). ممتاز!
🙋
الطالب
أستاذ — أفهم المفهوم. لكن لديّ تحدٍّ. حين أذهب لكتابة كود Java… كيف أجعل كائن Name يموت فعلًا حين يُدمَّر Student؟ وكيف أجعل Address يبقى حيًّا؟ مخطط الفئة (UML) يظهر الماسة ◆ أو ◇ — لكن كيف أُعبّر عن ذلك في الكود؟
👨‍🏫
الأستاذ
سؤال ممتاز — هذا بالضبط ما يجب أن تفهمه لتصبح مبرمجًا جيدًا. الإجابة قاعدة واحدة: كل شيء يتعلق بـأين تكتب new.

للـتركيب (Composition) ◆ — تكتب new Name(...) داخل مُنشئ (constructor) الـStudent. كائن Name هذا أنشأه الطالب، يملكه الطالب، ولا مرجع له خارجه. حين يختفي الطالب — يختفي الاسم معه. لا أحد آخر يحتفظ بمرجع إليه.

للـتجميع (Aggregation) ◇ — تُنشئ كائن Address خارجيًا، في main أو في مكان آخر، ثم تمرّره إلى المُنشئ (constructor). الآن يمكن لعدة طلاب الاحتفاظ بمرجع لنفس كائن العنوان. حين يُدمَّر طالب واحد، العنوان ما زال حيًّا — لأن الطلاب الآخرين ما زالوا يحتفظون بمرجعهم إليه.
🙋
الطلاب
آه! إذن السر في أين تُكتب new!
new داخل المُنشئ (constructor) → تركيب (Composition) ◆ → يموت مع المالك
new خارجيًا، الكائن يُمرَّر → تجميع (Aggregation) ◇ → يمكنه البقاء مستقلًا
👨‍🏫
الأستاذ
بالضبط! ✅ هذه هي القاعدة الوحيدة التي يجب عليك تطبيقها. انظر إلى الكود بعناية — مُنشئ Student يستقبل first وlast كمعاملَي String عاديَّين، ثم ينادي new Name(first, last) هناك في الداخل مباشرةً. هذا هو التركيب (Composition). أما للعنوان، فيستقبل كائن Address جاهزًا أُنشئ خارجيًا — هذا هو التجميع (Aggregation). مُنشئ واحد، علاقتان مختلفتان، يُعبَّر عنهما باختلاف صغير واحد في مكان كتابة new.
📄 Name.java
public class Name { private String firstName; private String lastName; public Name(String first, String last) { this.firstName = first; this.lastName = last; } public String getFullName() { return firstName + " " + lastName; } }
📄 Address.java
public class Address { private String street; private String city; private int unit; public Address(String street, String city, int unit) { this.street = street; this.city = city; this.unit = unit; } public String getCity() { return city; } public String getStreet() { return street; } }
📄 Student.java — التركيب (Composition) مقابل التجميع (Aggregation) في الكود
public class Student { private int id; private Name name; // ◆ تركيب (COMPOSITION) — سيُنشأ بالداخل private Address address; // ◇ تجميع (AGGREGATION) — سيُستقبل من الخارج // Name يُنشأ هنا داخل المُنشئ (constructor) → تركيب (Composition) // Address يُمرَّر من الخارج → تجميع (Aggregation) public Student(int id, String first, String last, Address address) { this.id = id; this.name = new Name(first, last); // ◆ تركيب: يُنشأ بالداخل this.address = address; // ◇ تجميع: كائن مشترك من الخارج } public String getFullName() { return name.getFullName(); } public Address getAddress() { return address; } // الاعتماد (Dependency): BMICalculator يُستخدَم مؤقتًا كمعامل دالة (Method Parameter) — لا يُخزَّن public double calculateBMI(BMICalculator calc) { return calc.getBMI(); } } // ── في main ──────────────────────────────────────────────────────────── // كائن عنوان واحد — مشترك بين عدة طلاب (تجميع Aggregation) Address sharedAddress = new Address("Al-Jami'a St", "Jeddah", 100); Student ali = new Student(1, "Ali", "Fahad", sharedAddress); Student khalid = new Student(2, "Khalid", "Nasser", sharedAddress); Student fahad = new Student(3, "Fahad", "Salem", sharedAddress); // الثلاثة يشتركون في نفس كائن العنوان — لو اختفى ali، يبقى khalid و fahad مع العنوان ✅ // كل طالب يمتلك كائن اسمه الخاص — أُنشئ داخل مُنشئه الخاص ◆
✅ قاعدة المبرمج الأساسية
  • التركيب (Composition) ◆أنشئ الكائن داخل المُنشئ (constructor) بـnew. الكائن حصري لهذا المالك.
  • التجميع (Aggregation) ◇استقبل الكائن من الخارج كمعامل للمُنشئ (constructor). يمكن مشاركته.
  • الاعتماد (Dependency) - - →استقبله كمعامل دالة (Method Parameter) فقط. لا يُخزَّن أبدًا كحقل (Field). ليس جزءًا من هوية (Identity) الكائن.
4
التجميع الذاتي (Self-Aggregation) — فئة تُشير إلى نفسها

ننتقل الآن إلى شيء مثير — التجميع الذاتي (Self-Aggregation). ماذا يعني؟ إنه تجميع بين كائنات من نفس الفئة. تحتوي الفئة على حقل (Field) بياناتي نوعه هو نفس الفئة. قد يبدو هذا غريبًا — لكنه طبيعي جدًا حين ترى المثال!

🏫 نقاش في الفصل — "كيف نُعيّن مشرفًا؟"
👨‍🏫
الأستاذ
دعوني أبسّط الأمر. لديّ ثلاثة طلاب: علي وخالد وفهد. تخيّل فقط أن لديهم رقمًا واسمًا — هذا كل شيء. أريد الآن أن أُعيّن علي مشرفًا على خالد وفهد. كيف أخزّن هذه المعلومة في فئة (Class) الـStudent؟
🙋
الطالب
أستاذ، لماذا لا نضيف حقلًا (Field) يُسمى supervisorId من نوع int؟ نخزّن فقط رقم المشرف!
👨‍🏫
الأستاذ
قد ينجح ذلك — لكن فكّر في الأمر. ماذا لو أردت في يوم ما معرفة عمر المشرف؟ أو معدّله؟ أو عنوانه؟ أي تفصيل عنه؟ لو خزّنت الرقم فقط، ستضطر إلى البحث بين كل الطلاب في كل مرة للعثور على تلك المعلومة. ستصبح الأمور معقدة جدًا وبطيئة!
📄 السيناريو 1 — supervisorId : int  ❌ نهج محدود
public class Student { private int id; private String name; private int supervisorId; // يخزّن الرقم فقط — لا تفاصيل أخرى public Student(int id, String name) { this.id = id; this.name = name; } public void setSupervisorId(int supervisorId) { this.supervisorId = supervisorId; } public int getSupervisorId() { return supervisorId; } public int getId() { return id; } public String getName() { return name; } } // ── في main ────────────────────────────────────────────────────────── Student ali = new Student(1, "Ali"); Student khaled = new Student(2, "Khaled"); Student fahad = new Student(3, "Fahad"); khaled.setSupervisorId(1); // رقم علي fahad.setSupervisorId(1); // رقم علي // ❌ المشكلة: للحصول على اسم المشرف يجب البحث يدويًا! int supId = khaled.getSupervisorId(); // يُرجع 1 — لكن ماذا بعد؟ // تحتاج مصفوفة أو حلقة للعثور على ذلك الطالب — بطيء جدًا! Student[] allStudents = {ali, khaled, fahad}; for (Student s : allStudents) { if (s.getId() == supId) { System.out.println(s.getName()); // "Ali" — لكن بأي ثمن؟ } }
👨‍🏫
الأستاذ
الآن — فكّر! المشرف هو أيضًا طالب، أليس كذلك؟ المشرف هو Student. إذن… لماذا لا نُعرّف حقل (Field) بيانات يُسمى supervisor، لكن نجعل نوعه Student؟ هذا يُعطيك كائن المشرف بالكامل بكل تفاصيله!
🙋
الطلاب
لحظة — حقل (Field) من نوع Student داخل فئة (Class) الـStudent نفسها؟! 🤯 كائنات داخل كائنات من نفس النوع!
👨‍🏫
الأستاذ
بالضبط! 🎉 هذا ما يُسمى التجميع الذاتي (Self-Aggregation)! أمر رائع — كائنات داخل كائنات من نفس الفئة. دعوني أريكم كيف يعمل في الكود.
Student -id : int -name : String -supervisor : Student +Student(id: int, name: String) +setSupervisor(s: Student) : void +getSupervisor() : Student +getId() : int +getName() : String supervisor 0..1 0..1

التجميع الذاتي (Self-aggregation): Student لديها حقل (Field) من نوع Student — 0..1 تعني أن للطالب صفرًا أو مشرفًا واحدًا

📄 السيناريو 2 — supervisor : Student  ✅ التجميع الذاتي (Self-aggregation) — النهج الصحيح
public class Student { private int id; private String name; private Student supervisor; // ← تجميع ذاتي (self-aggregation): نفس الفئة (Class) كنوع الحقل (Field)! public Student(int id, String name) { this.id = id; this.name = name; // supervisor يبدأ بـ null — يمكن تعيينه لاحقًا } public void setSupervisor(Student supervisor) { this.supervisor = supervisor; } public Student getSupervisor() { return supervisor; } public int getId() { return id; } public String getName() { return name; } } // ── في main ────────────────────────────────────────────────────────── Student ali = new Student(1, "Ali"); Student khaled = new Student(2, "Khaled"); Student fahad = new Student(3, "Fahad"); // تعيين علي مشرفًا لخالد وفهد khaled.setSupervisor(ali); fahad.setSupervisor(ali); // الآن يمكن الوصول لكل تفاصيل المشرف عبر سلسلة الكائنات! System.out.println(khaled.getSupervisor().getName()); // "Ali" System.out.println(fahad.getSupervisor().getId()); // 1

📦 الكائنات في الذاكرة — مراجع متسلسلة:

khaled : Student
id2
name"Khaled"
supervisor→ ali
ali : Student
id1
name"Ali"
supervisornull
🔮 ستقابل هذا مجددًا — القوائم المترابطة (Linked Lists)!

ستلتقي بالتجميع الذاتي (Self-Aggregation) مجددًا في مساق هياكل البيانات الفصل القادم، حين تدرس القوائم المترابطة (Linked Lists). كل Node تحتفظ ببعض البيانات ومرجع للـNode التالية — وهذا بالضبط نفس نمط التجميع الذاتي! المفاهيم التي تتعلمها الآن هي الأساس.

5
التعددية (Multiplicity) — كم عدد الكائنات المشاركة؟

قبل الانتقال إلى الموضوع التالي، شرح الأستاذ مفهومًا آخر في مخطط الفئة (UML Class Diagram) — التعددية (Multiplicity). هذا يُجيب عن السؤال: "كم عدد الكائنات من فئة معينة مشاركة في علاقة ما؟"

🏫 نقاش في الفصل — ما هي التعددية (Multiplicity)؟
👨‍🏫
الأستاذ
في مخطط الفئة (UML)، حين نرسم علاقة بين فئتين، يمكن أن تكون العلاقة متعدد-لمتعدد أو واحد-لمتعدد. يجب أن نوضح هذا للطالب. لكل فئة مشاركة في العلاقة، نكتب رقمًا — أو نطاقًا — عند طرفها من خط العلاقة. هذا ما يُسمى التعددية (Multiplicity).
👨‍🏫
الأستاذ
على سبيل المثال — المعلم (Teacher) يمكنه تدريس صفر إلى ثلاثة مقررات (0..3). كل مقرر (Course) لديه معلم واحد بالضبط (1). يجب أن يضم المقرر ما بين 5 و60 طالبًا (5..60). والطالب يمكنه التسجيل في عدد كبير من المقررات (*). هذه هي التعددية (Multiplicity)!
📐 ما هي التعددية (Multiplicity)?

التعددية (Multiplicity) تُحدد عدد النسخ من فئة يمكن أن ترتبط بنسخة واحدة من فئة أخرى. تُكتب عند طرفَي خط العلاقة في مخطط الفئة (UML). كل فئة مشاركة في العلاقة لها تعدديتها المحددة.

الرمزالمعنىمثال
1واحد بالضبطللمقرر معلم واحد بالضبط
0..1صفر أو واحد (اختياري)الطالب قد يكون لديه صفر أو مشرف واحد
* أو 0..*صفر أو أكثرالطالب يمكنه التسجيل في عدد كبير من المقررات
1..*واحد أو أكثريجب أن تضم الكلية معلمًا واحدًا على الأقل
5..60بين 5 و60قسم المقرر يضم 5 إلى 60 طالبًا
0..3صفر إلى ثلاثةالمعلم يُدرّس 0 إلى 3 مقررات

🎓 مثال: Teacher → Course → Student

Teacher - name : String 1 0..3 Course - courseCode : String * 5..60 Student - id : int 1 teacher teaches 0..3 courses a student takes many (*) courses; each course has 5..60 students
📝 كيف تقرأ أرقام التعددية (Multiplicity)

اقرأ كل رقم من منظور الفئة الأخرى.

  • الرقم 1 بجانب Teacher يعني: "من منظور المقرر (Course)، لديه معلم واحد بالضبط."
  • الرقم 0..3 بجانب Course يعني: "من منظور المعلم (Teacher)، يُدرّس 0 إلى 3 مقررات."
  • الرقم 5..60 بجانب Student يعني: "من منظور المقرر (Course)، يضم 5 إلى 60 طالبًا مسجلًا."
  • الرقم * بجانب Course يعني: "من منظور الطالب (Student)، يمكنه التسجيل في أي عدد من المقررات."
الملخص
🎉 ما تناولناه في هذا الجزء
🔧
3 علاقات رئيسية: الاعتماد · التجميع · الوراثة (Dependency · Aggregation · Inheritance)
"يستخدم" · "يمتلك" · "هو نوع من" — بسيط! التجميع (Aggregation) له قوتان: ضعيف (تجميع) وقوي (تركيب).
🔧
الاعتماد (Dependency) — "يستخدم"
استخدام مؤقت كمعامل دالة (Method Parameter). لا يُخزَّن كحقل (Field). ليس جزءًا من هوية (Identity) الكائن. يمكن استبداله بدالة ثابتة (static method).
📦
التجميع (Aggregation) ◇ — "has-a"، ضعيف
الكائن المحتوى يبقى مستقلًا ويمكن مشاركته (مثل: Address). يُنشأ خارجيًا ويُمرَّر. ماسة مفتوحة في مخطط الفئة (UML).
💎
التركيب (Composition) ◆ — "owns-a"، قوي
الكائن المحتوى لا يمكنه الوجود بدون المالك (مثل: Name). يُنشأ داخل المُنشئ (constructor) بـnew. ماسة مملوءة في مخطط الفئة (UML).
🔄
التجميع الذاتي (Self-Aggregation)
فئة (Class) تحتوي على حقل (Field) من نوعها الخاص (مثل: Student supervisor داخل Student). كائنات داخل كائنات من نفس النوع. أساس القوائم المترابطة (Linked Lists)!
🔢
التعددية (Multiplicity)
تُحدد عدد الكائنات من فئة مشاركة في علاقة. تُكتب عند طرفَي خط العلاقة في مخطط الفئة (UML): 1، 0..1، *، 1..*، 5..60.