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

الـ Composition مقابل الـ Inheritance

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

تنشئ الوراثة علاقة «is-a»: إذ إن Dog هو Animal. ولكن ماذا لو احتاجت فئة ما إلى وظائف من شيء ليست هي إياه؟ هنا يأتي دور composition، إذ تنشئ بدلاً من ذلك علاقة «has-a».

مع التركيب، يحتوي الفئة على مثيلات من فئات أخرى بوصفها حقولًا بدلًا من توسيعها:

// نهج الوراثة: Car هي Engine (غير منطقي)
class Car extends Engine { }

// نهج التركيب: Car تمتلك Engine (منطقي)
class Car {
    private Engine engine;
    
    public Car(Engine engine) {
        this.engine = engine;
    }
    
    public void start() {
        engine.ignite();
    }
}

يوفّر Composition العديد من المزايا. يمكنك تغيير الكائن المركّب في وقت التشغيل، مثل تبديل المحركات.

يمكنك أيضًا دمج السلوكيات من عدة فئات، وهو أمر لا يتيحه inheritance في Java. ولا تكشف الفئة المركبة إلا عن methods التي تختارها، مما يوفر تغليفًا أفضل.

إرشاد عملي: استخدم Inheritance عندما تكون هناك علاقة حقيقية من نوع «is-a» وتُمثّل الفئة الفرعية حقًا إصدارًا متخصصًا من الفئة الأصلية.

استخدم composition عندما تحتاج إلى استخدام وظائف class أخرى من دون أن تكون من ذلك النوع. يتبع العديد من المطورين ذوي الخبرة المبدأ التالي: "favor composition over inheritance" لأنه يؤدي إلى تعليمات برمجية أكثر مرونة وقابلية للصيانة.

challenge icon

التحدي

سهل

لنَبْنِ نظام كمبيوتر باستخدام composition لتوضيح علاقة "has-a". بدلًا من أن يرث Computer من مكوّناته (وهو ما لن يكون منطقيًا)، سيحتوي الكمبيوتر على الأجزاء التي يحتاج إليها ليعمل.

ستنظّم كودك عبر ثلاثة ملفات:

  • Processor.java: أنشئ class تمثّل وحدة CPU. تحتوي Processor على حقلي brand وspeedGHz خاصّين: الأول من النوع String والثاني من النوع double. أضف constructor لتهيئة الحقلين، وgetter methods لكل منهما. أضف method باسم process() تُرجع String بالشكل التالي: [brand] processing at [speedGHz] GHz
  • Memory.java: أنشئ class تمثّل ذاكرة RAM. تحتوي Memory على حقلي type وsizeGB خاصّين: الأول من النوع String (مثل "DDR4" أو "DDR5") والثاني من النوع int. أضف constructor وgetters، بالإضافة إلى method باسم load() تُرجع: Loading [sizeGB]GB [type] memory
  • Computer.java: هنا تتألق composition! يجب أن تحتوي class Computer على Processor وMemory بوصفهما حقلي fields خاصّين. فهي لا تمدّهما، بل تحتويهما. أضف constructor يقبل كلا المكوّنين. أضف method باسم boot() تُرجع String متعددة الأسطر توضّح بدء تشغيل الكمبيوتر باستخدام مكوّناته:
    Booting computer...
    [processor.process() result]
    [memory.load() result]
    System ready!
    وأضف أيضًا method باسم getSpecs() تُرجع: Specs: [processor brand] CPU, [memory sizeGB]GB [memory type]
  • Main.java: اجعل نظامك المؤلَّف يعمل! ستتلقى أربع inputs: brand المعالج (String)، وسرعة المعالج (double)، ونوع الذاكرة (String)، وحجم الذاكرة (int).

    أنشئ Processor وMemory باستخدام هذه القيم، ثم compose بينهما داخل Computer. اطبع نتيجة استدعاء boot()، ثم اطبع نتيجة getSpecs().

ستتلقى أربع inputs بالترتيب: brand المعالج، وسرعة المعالج (GHz)، ونوع الذاكرة، وحجم الذاكرة (GB).

لاحظ كيف تفوّض Computer العمل إلى مكوّناته بدلًا من محاولة أن تكون هذه المكوّنات. هذه هي قوة composition! يمكن لـ Computer استخدام أي Processor أو Memory تمنحه إياه، مما يجعل التصميم مرنًا وواقعيًا.

جرّب بنفسك

import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        
        // قراءة المدخلات
        String processorBrand = scanner.nextLine();
        double processorSpeed = Double.parseDouble(scanner.nextLine());
        String memoryType = scanner.nextLine();
        int memorySize = Integer.parseInt(scanner.nextLine());
        
        // TODO: إنشاء Processor باستخدام العلامة التجارية والسرعة
        
        // TODO: إنشاء Memory باستخدام النوع والحجم
        
        // TODO: إنشاء Computer عن طريق تجميع Processor و Memory
        
        // TODO: طباعة نتيجة boot()
        
        // TODO: طباعة نتيجة getSpecs()
    }
}
quiz iconاختبر نفسك

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

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

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