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

الواجهة مقابل الـ Abstract Class

جزء من قسم Object Oriented Programming في رحلة PHP على Coddy — الدرس 30 من 91.

بعد أن تعلمت كلاً من interfaces و abstract classes، دعنا نوضح متى نستخدم كل منهما. فبينما قد يبدوان متشابهين، إلا أنهما يخدمان أغراضاً مختلفة ولهما قدرات متميزة.

الميزة الواجهة (Interface) الفئة المجردة (Abstract Class)
الدوال (Methods) تواقيع الدوال فقط كل من الدوال المجردة والملموسة (concrete)
الخصائص (Properties) الثوابت فقط يمكن أن تحتوي على خصائص
الوراثة المتعددة يمكن للفئة تنفيذ واجهات متعددة يمكن للفئة وراثة فئة واحدة فقط
المنشئ (Constructor) يمكنها التصريح عن توقيع __construct (بدون جسم الدالة) يمكنها تعريف منشئ مع جسم للدالة

استخدم interface عندما تريد تعريف عقد يمكن للأصناف غير المرتبطة الوفاء به. على سبيل المثال، قد يقوم كل من Printer و Logger بتنفيذ واجهة Writable، على الرغم من أنهما لا يشتركان في أي سلف مشترك.

استخدم فئة مجردة (abstract class) عندما يكون لديك فئات ذات صلة تشترك في كود مشترك. إذا كانت فئتا Dog و Cat تحتاجان إلى نفس تنفيذ دالة eat()، فإن فئة Animal مجردة تتيح لك كتابة هذا الكود مرة واحدة.

يمكنك أيضاً الجمع بين كلا النهجين. يمكن لـ class أن يقوم بـ extend لـ abstract class بينما يقوم بـ implement لعدة interfaces:

<?php
abstract class Animal {
    protected $name;
    
    public function eat() {
        return $this->name . " is eating";
    }
}

interface Swimmable {
    public function swim();
}

class Duck extends Animal implements Swimmable {
    public function __construct($name) {
        $this->name = $name;
    }
    
    public function swim() {
        return $this->name . " is swimming";
    }
}

نقطة رئيسية: اختر interfaces لتعريف القدرات عبر classes غير مرتبطة. اختر abstract classes عندما تحتاج إلى مشاركة الكود بين classes مرتبطة.

challenge icon

التحدي

سهل

لنقم ببناء نظام مركبات يوضح متى يجب استخدام فئة مجردة (abstract class) مقابل الواجهة (interface) — وكيفية الجمع بين كلا النهجين بفعالية.

ستقوم بإنشاء نظام تشترك فيه المركبات في خصائص عامة من خلال الوراثة، ولكن يمكنها أيضاً اكتساب قدرات إضافية من خلال الواجهات. هذا يحاكي قرارات التصميم في العالم الحقيقي: السيارة "هي" مركبة (وراثة)، ولكن لديها أيضاً "القدرة" على أن تكون قابلة للتزود بالوقود (واجهة).

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

  • Refuelable.php — قم بتعريف واجهة Refuelable مع توقيع طريقة واحدة: refuel($amount). يمكن تطبيق هذه القدرة على أي فئة تحتاج إلى التزود بالوقود، بغض النظر عن تسلسل الوراثة الخاص بها.
  • Vehicle.php — قم بتعريف فئة مجردة Vehicle توفر وظائف مشتركة لجميع المركبات. يجب أن تحتوي على خاصية محمية $brand وخاصية محمية $fuelLevel (تبدأ من 0). يقبل المنشئ (constructor) اسم العلامة التجارية. قم بتضمين طريقة ملموسة getBrand() تعيد العلامة التجارية، وطريقة ملموسة getFuelLevel() تعيد مستوى الوقود الحالي، وطريقة مجردة drive() يجب على الفئات التابعة تنفيذها.
  • Car.php — أنشئ فئة Car ترث من Vehicle وتنفذ Refuelable. قم بتضمين كلا الملفين المطلوبين في الأعلى. يقبل المنشئ العلامة التجارية ويمررها إلى منشئ الفئة الأب. قم بتنفيذ drive() لتعيد "[brand] car is driving". قم بتنفيذ refuel($amount) لإضافة الكمية إلى مستوى الوقود وإعادة "[brand] refueled to [fuelLevel] liters".
  • main.php — قم بتضمين ملف Car. ستتلقى مدخلين: اسم العلامة التجارية وكمية الوقود (قم بتحويلها إلى عدد صحيح). أنشئ Car بالعلامة التجارية. اطبع نتيجة drive() في السطر الأول، ثم اطبع نتيجة refuel() مع كمية الوقود في السطر الثاني.

يوضح هذا التحدي التمييز الرئيسي: الفئة المجردة Vehicle تشارك الكود وتؤسس علاقة "is-a"، بينما تضيف واجهة Refuelable قدرة يمكن تطبيقها على فئات أخرى غير مرتبطة (مثل المولد أو القارب) دون مطالبتها بأن تكون مركبات.

جرّب بنفسك

<?php
require_once 'Car.php';

// قراءة المدخلات
$brand = trim(fgets(STDIN));
$fuelAmount = intval(trim(fgets(STDIN)));

// TODO: إنشاء Car باستخدام brand

// TODO: طباعة نتيجة drive() في السطر الأول

// TODO: طباعة نتيجة refuel() مع fuel amount في السطر الثاني
?>
quiz iconاختبر نفسك

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

جميع دروس Object Oriented Programming