Menu
Coddy logo textTech
flag Ar iconالعربيةdown icon

الـ Aggregation مقابل الـ Composition

جزء من قسم البرمجة كائنية التوجه في رحلة Java على Coddy. الدرس 48 من 87.

كلٌّ من aggregation و composition هما شكلان من علاقات "has-a"، لكنهما يختلفان في جانب حاسم واحد: الملكية والاعتماد على دورة الحياة.

في composition، لا يمكن للكائن المحتوى أن يوجد independently عن الحاوية. عند تدمير الحاوية، تُدمَّر أجزاؤها أيضًا. فكّر في كائنات 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();  // `House` ينشئ ويمتلك `rooms`
        }
    }
}

في aggregation، يمكن للكائن المحتوى أن يوجد بشكل مستقل. تستخدم الحاوية الكائن، لكنها لا تتحكم في دورة حياته. تأمل Team وكائنات Player التابعة له: يوجد اللاعبون حتى من دون فريق:

class Team {
    private List<Player> players;
    
    public Team() {
        players = new ArrayList<>();
    }
    
    public void addPlayer(Player player) {
        players.add(player);  // يستخدم Team اللاعبين الموجودين
    }
}

التمييز الأساسي: في composition، ينشئ الحاوي أجزائه داخليًا. في aggregation، تُمرَّر الكائنات من الخارج. عند حذف Team، تظل كائنات Player موجودة. عند حذف House، تختفي كائنات Room الخاصة بـ its أيضًا.

challenge icon

التحدي

سهل

لنبنِ نظامًا للمقررات الجامعية يوضح كلاً من aggregation وcomposition. سترى كيف يمتلك Course كائنات Lecture الخاصة به (composition)، بينما يكتفي بالإشارة إلى كائنات Professor التي توجد independently (aggregation).

ستنظّم شيفرتك عبر أربعة ملفات:

  • Professor.java: أنشئ class تمثّل أستاذًا يمكنه أن يوجد independently عن أي مقرر. لدى Professor حقلان private: name (String) وdepartment (String). أضف constructor لتهيئة الحقلين، وgetter methods لكل منهما. أعد تعريف toString() بحيث تُرجع: Prof. [name] ([department])
  • Lecture.java: أنشئ class تمثّل محاضرة لا يكون لها معنى إلا ضمن مقرر. لدى Lecture حقلان private: topic (String) وdurationMinutes (int). أضف constructor وgetters. أعد تعريف toString() بحيث تُرجع: Lecture: [topic] ([durationMinutes] min)
  • Course.java: هنا ستوضح كلتا العلاقتين! لدى Course ثلاثة حقول private: title (String)، وprofessor (Professor - aggregation)، وlectures (array من كائنات Lecture - composition).

    يأخذ constructor عنوان المقرر، وكائن Professor (يمرَّر من الخارج - aggregation)، وعدد المحاضرات. داخل constructor، أنشئ array الخاصة بـ Lecture واملأها بكائنات Lecture جديدة. لكل محاضرة عند الفهرس i، أنشئها بموضوع "Topic " + (i + 1) وduration مقدارها 45 دقيقة. يوضح هذا الإنشاء الداخلي composition.

    أضف method باسم getInfo() تُرجع String متعددة الأسطر:

    Course: [title]
    Instructor: [professor.toString()]
    Lectures: [number of lectures]

    أضف أيضًا method باسم listLectures() تُرجع String تحتوي على استدعاء toString() لكل محاضرة في سطر مستقل.

  • Main.java: اجمع كل شيء معًا! ستتلقى أربعة inputs: اسم الأستاذ (String)، وقسم الأستاذ (String)، وعنوان المقرر (String)، وعدد المحاضرات (int).

    أولًا، أنشئ Professor بالاسم والقسم المحددين. يوجد هذا الأستاذ independently. ثم أنشئ Course، مع تمرير العنوان، والأستاذ (aggregation)، وعدد المحاضرات (حيث سينشئ المقرر محاضراته الخاصة internally - composition).

    اطبع نتيجة getInfo()، ثم اطبع سطرًا فارغًا، ثم اطبع نتيجة listLectures().

ستتلقى أربعة inputs بالترتيب: اسم الأستاذ، والقسم، وعنوان المقرر، وعدد المحاضرات.

لاحظ الفرق الأساسي: يتم إنشاء Professor خارج Course ثم تمريره إليه (aggregation: يمكن للأستاذ تدريس مقررات أخرى أو أن يوجد دون هذا المقرر). أما Lectures فتُنشأ داخل constructor الخاص بـ Course (composition. فهي تنتمي حصريًا إلى هذا المقرر، ولن توجد من دونه).

جرّب بنفسك

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 (يوجد بشكل مستقل - تجميع)
        
        // TODO: إنشاء كائن Course، مع تمرير الأستاذ (تجميع)
        // ستقوم الدورة بإنشاء المحاضرات الخاصة بها داخليًا (تركيب)
        
        // TODO: طباعة نتيجة getInfo()
        
        // TODO: طباعة سطر فارغ
        
        // TODO: طباعة نتيجة listLectures()
        
    }
}
quiz iconاختبر نفسك

يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.

جميع دروس البرمجة كائنية التوجه

تدرّب بنفسك: مترجم Java عبر الإنترنت