Menu
CoddyTech

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

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

كلٌّ من التجميع والتركيب شكلٌ من أشكال علاقات «يمتلك»، لكنهما يختلفان في جانب جوهري واحد: الملكية والاعتماد على دورة الحياة.

في التركيب، لا يمكن للكائن المحتوى أن يوجد مستقلًا عن الحاوية. عندما تُدمَّر الحاوية، تُدمَّر أجزاؤها أيضًا. فكّر في 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 الغرف ويمتلكها
        }
    }
}

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

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

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

challenge icon

التحدي

سهل

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

ستوزّع شيفرتك على أربعة ملفات:

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

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

    أضف أيضًا دالة getInfo() تُرجع سلسلة نصية متعددة الأسطر:

    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 (التركيب). فهي تنتمي حصريًا إلى هذا المقرر ولن تكون موجودة من دونه).

جرّب بنفسك

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 عبر الإنترنت