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

الأصناف المجردة مقابل الواجهات

جزء من قسم Object Oriented Programming في رحلة Java على Coddy — الدرس 38 من 87.

الآن بعد أن فهمت كلًا من الفئات المجردة (abstract classes) والواجهات (interfaces)، يطرح سؤال شائع نفسه: متى يجب عليك استخدام أحدهما بدلاً من الآخر؟ يعتمد الاختيار على ما تحاول نمذجته.

استخدم abstract class عندما تشترك الفئات في قاعدة مشتركة ذات حالة أو سلوك مشترك. يمكن أن تحتوي الفئات المجردة على instance variables، و constructors، ومزيج من الأساليب abstract و concrete:

public abstract class Animal {
    protected String name;  // حالة مشتركة
    
    public Animal(String name) {
        this.name = name;
    }
    
    public void sleep() {  // سلوك مشترك
        System.out.println(name + " is sleeping");
    }
    
    public abstract void makeSound();  // يجب تنفيذه
}

استخدم واجهة (interface) عندما تريد تعريف قدرة يمكن للأصناف (classes) غير المرتبطة مشاركتها. تركز الواجهات على ما يمكن للكائن فعله، وليس على ماهيته:

public interface Flyable {
    void fly();
}

// يمكن للفئات غير المرتبطة مشاركة هذه القدرة
class Bird extends Animal implements Flyable { ... }
class Airplane implements Flyable { ... }
class Drone implements Flyable { ... }

إليك مقارنة سريعة:

الميزةالفئة المجردة (Abstract Class)الواجهة (Interface)
متغيرات النسخة (Instance variables)نعمالثوابت فقط
المنشئات (Constructors)نعملا
الوراثة المتعددةلا (single extends)نعم (multiple implements)
معدلات الوصول (Access modifiers)أي نوععام فقط (للطرق المجردة - abstract methods)

إرشادات عملية: إذا وجدت نفسك تنشئ abstract class تحتوي فقط على abstract methods وبدون state، فمن المرجح أن تكون الـ interface هي الخيار الأفضل.

challenge icon

التحدي

سهل

لنقم ببناء نظام مركبات يوضح متى يجب استخدام الفئات المجردة (abstract classes) مقابل الواجهات (interfaces). ستقوم بنمذجة مركبات تشترك في حالة وسلوك عام من خلال فئة مجردة، مع إضافة قدرات اختيارية من خلال الواجهات.

ستقوم بتنظيم الكود الخاص بك عبر خمسة ملفات:

  • Vehicle.java: أنشئ فئة مجردة (abstract class) تعمل كأساس لجميع المركبات. كل مركبة لديها حقل brand (String) وحقل year (int). قم بتضمين منشئ (constructor) لتهيئة كلا الحقلين، وطرق جلب (getter methods) لكل منهما، وطريقة مجردة startEngine() تعيد String. أضف أيضاً طريقة ملموسة (concrete method) باسم getInfo() تعيد: [brand] ([year]). هذه حالة استخدام مثالية للفئة المجردة—المركبات تشترك في الحالة (brand، year) وبعض السلوك (getInfo)، ولكن كل واحدة تبدأ محركها بشكل مختلف.
  • Convertible.java: قم بتعريف واجهة (interface) للمركبات التي يمكنها تحويل سقفها. هذه القدرة ليست مرتبطة بما تكونه المركبة—بل هي شيء يمكن لبعض المركبات القيام به. صرح عن طريقتين: openRoof() و closeRoof()، وكلاهما يعيد String.
  • Car.java: أنشئ فئة ترث من Vehicle وتنفذ Convertible. تمتلك السيارة (Car) حقلاً إضافياً numDoors (int). استخدم super لتهيئة الحقول الموروثة. قم بتنفيذ startEngine() لتعيد: [brand] car engine started. قم بتنفيذ openRoof() لتعيد: [brand] roof opening و closeRoof() لتعيد: [brand] roof closing.
  • Motorcycle.java: أنشئ فئة ترث من Vehicle ولكنها لا تنفذ Convertible—فالدراجات النارية ليس لها سقوف! تمتلك الدراجة النارية (Motorcycle) حقلاً hasSidecar (boolean). قم بتنفيذ startEngine() لتعيد: [brand] motorcycle engine roaring.
  • Main.java: اجمع كل شيء معاً لإظهار الفرق بين الفئات المجردة والواجهات. ستتلقى أربعة مدخلات: ماركة السيارة، سنة صنع السيارة، ماركة الدراجة النارية، وسنة صنع الدراجة النارية.

    أنشئ كائن Car (بـ 4 أبواب) وكائن Motorcycle (بدون عربة جانبية). أولاً، استعرض سلوك الفئة المجردة المشترك عن طريق طباعة getInfo() لكلتا المركبتين—فكلاهما يرث هذا من Vehicle. ثم اطبع نتيجة startEngine() لكل منهما—لاحظ كيف تنفذ كل نوع من المركبات هذا بشكل مختلف.

    أخيراً، استعرض قدرة الواجهة: بما أن Car فقط هي التي تنفذ Convertible، قم باستدعاء وطباعة openRoof() متبوعة بـ closeRoof() على السيارة فقط.

ستتلقى أربعة مدخلات: ماركة السيارة (String)، سنة صنع السيارة (int)، ماركة الدراجة النارية (String)، وسنة صنع الدراجة النارية (int).

يجب أن يظهر مخرجك ستة أسطر في المجموع. لاحظ كيف تشترك كلتا المركبتين في حالة الفئة المجردة وطريقة getInfo()، ولكن السيارة فقط هي التي تمتلك قدرة التحويل—وهذا يوضح متى يجب استخدام كل نهج!

ورقة مرجعية

استخدم فئة مجردة (abstract class) عندما تشترك الفئات في قاعدة مشتركة مع حالة أو سلوك مشترك. يمكن أن تحتوي الفئات المجردة على متغيرات مثيل (instance variables)، ومنشئات (constructors)، ومزيج من الأساليب المجردة والملموسة:

public abstract class Animal {
    protected String name;  // حالة مشتركة
    
    public Animal(String name) {
        this.name = name;
    }
    
    public void sleep() {  // سلوك مشترك
        System.out.println(name + " is sleeping");
    }
    
    public abstract void makeSound();  // يجب تنفيذها
}

استخدم واجهة (interface) عندما تريد تحديد قدرة يمكن للفئات غير المرتبطة مشاركتها. تركز الواجهات على ما يمكن للكائن فعله، وليس على ماهيته:

public interface Flyable {
    void fly();
}

// يمكن للفئات غير المرتبطة مشاركة هذه القدرة
class Bird extends Animal implements Flyable { ... }
class Airplane implements Flyable { ... }
class Drone implements Flyable { ... }
الميزةالفئة المجردة (Abstract Class)الواجهة (Interface)
متغيرات المثيل (Instance variables)نعمالثوابت فقط
المنشئات (Constructors)نعملا
الوراثة المتعددةلا (توسيع واحد فقط single extends)نعم (تنفيذ متعدد multiple implements)
معدلات الوصول (Access modifiers)أي نوععامة فقط (للأساليب المجردة)

إرشاد: إذا كنت تقوم بإنشاء فئة مجردة تحتوي فقط على أساليب مجردة وبدون حالة، فمن المرجح أن تكون الواجهة هي الخيار الأفضل.

جرّب بنفسك

import java.util.Scanner;

class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        
        // قراءة المدخلات
        String carBrand = scanner.nextLine();
        int carYear = scanner.nextInt();
        scanner.nextLine(); // استهلاك السطر الجديد
        String motorcycleBrand = scanner.nextLine();
        int motorcycleYear = scanner.nextInt();
        
        // TODO: إنشاء Car بـ 4 أبواب
        
        // TODO: إنشاء Motorcycle بدون عربة جانبية (false)
        
        // TODO: طباعة getInfo() لكلا المركبتين (يوضح سلوك abstract class المشترك)
        
        // TODO: طباعة startEngine() لكلا المركبتين (يوضح التنفيذات المختلفة)
        
        // TODO: طباعة openRoof() و closeRoof() للـ car فقط (يوضح قدرة interface)
    }
}
quiz iconاختبر نفسك

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

جميع دروس Object Oriented Programming