الواجهة مقابل الـ Abstract Class
جزء من قسم البرمجة كائنية التوجه في رحلة PHP على Coddy. الدرس 30 من 91.
الآن بعد أن تعلّمت كلًّا من الواجهات والفئات المجرّدة، لنوضّح متى تستخدم كلًّا منهما. رغم أنهما قد تبدوان متشابهتين، فإن لكلٍّ منهما أغراضًا مختلفة وإمكانات مميّزة.
| الميزة | الواجهة | الفئة المجرّدة |
|---|---|---|
| الأساليب | توقيعات الأساليب فقط | أساليب مجرّدة وملموسة |
| الخصائص | الثوابت فقط | يمكن أن تحتوي على خصائص |
| التوريث المتعدد | يمكن للفئة أن تنفّذ العديد منها | يمكن للفئة أن تمدّد فئة واحدة فقط |
| الباني | يمكنها التصريح بتوقيع __construct (من دون جسم) | يمكنها تعريف باني مع جسم |
استخدم interface عندما تريد تعريف عقد يمكن للأصناف غير المرتبطة الوفاء به. على سبيل المثال، قد يطبّق كلٌّ من Printer وLogger واجهة Writable، رغم أنهما لا يشتركان في سلف مشترك.
استخدم abstract class عندما تكون لديك فئات مترابطة تشترك في تعليمات برمجية مشتركة. إذا كانت فئتا Dog وCat تحتاجان كلتاهما إلى تطبيق أسلوب eat() نفسه، تتيح لك فئة Animal مجردة كتابة ذلك التعليمات البرمجية مرة واحدة.
يمكنك أيضًا الجمع بين النهجين. يمكن لفئة أن ترث من فئة abstract مع تنفيذ واجهات متعددة:
<?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";
}
}
النقطة الأساسية: اختر الواجهات لتعريف القدرات عبر الفئات غير المرتبطة. اختر الفئات المجرّدة عندما تحتاج إلى مشاركة التعليمات البرمجية بين الفئات المرتبطة.
التحدي
سهللنبنِ نظامًا للمركبات يوضّح متى نستخدم abstract class بدلًا من interface، وكيف ندمج النهجين بفعالية.
ستنشئ نظامًا تشترك فيه المركبات في property مشتركة من خلال الوراثة، ولكن يمكنها أيضًا اكتساب إمكانات إضافية من خلال interface. وهذا يحاكي قرارات التصميم في العالم الحقيقي: السيارة «is a» مركبة (وراثة)، لكنها أيضًا «has the capability» لأن تكون قابلة لإعادة التزوّد بالوقود (interface).
نظّم كودك عبر أربعة ملفات:
Refuelable.php: عرّف interface باسمRefuelableمع method واحدة فقط:refuel($amount). يمكن تطبيق هذه الإمكانية على أي class تحتاج إلى إعادة التزوّد بالوقود، بغضّ النظر عن تسلسل الوراثة الخاص بها.Vehicle.php: عرّف abstract class باسمVehicleتوفّر الوظائف المشتركة لجميع المركبات. يجب أن تحتوي على property محمية باسم$brandوproperty محمية باسم$fuelLevel(تبدأ بالقيمة 0). يستقبل constructor اسم العلامة التجارية. أدرج method ملموسة باسمgetBrand()تُرجع العلامة التجارية، وmethod ملموسة باسمgetFuelLevel()تُرجع مستوى الوقود الحالي، وmethod abstract باسمdrive()يجب على الفئات الابنة تنفيذها.Car.php: أنشئ class باسمCarيمتد منVehicleويطبّقRefuelable. أدرج الملفين المطلوبين في الأعلى. يستقبل constructor العلامة التجارية ويمرّرها إلى constructor الخاص بـ parent. نفّذdrive()لإرجاع"[brand] car is driving". نفّذrefuel($amount)لإضافة المبلغ إلى مستوى الوقود وإرجاع"[brand] refueled to [fuelLevel] liters".main.php: أدرج ملف Car. ستتلقى مدخلين: اسم علامة تجارية ومقدار الوقود (حوّله إلى عدد صحيح). أنشئCarباستخدام العلامة التجارية. اطبع نتيجةdrive()في السطر الأول، ثم اطبع نتيجةrefuel()مع مقدار الوقود في السطر الثاني.
يوضّح هذا التحدي التمييز الأساسي: تشارك abstract class المسماة Vehicle الكود وتؤسس علاقة «is-a»، بينما يضيف interface المسمى Refuelable إمكانية يمكن أن تنطبق على فئات أخرى غير مرتبطة (مثل مولّد أو قارب) من دون الحاجة إلى أن تكون مركبات.
جرّب بنفسك
<?php
require_once 'Car.php';
// اقرأ المدخلات
$brand = trim(fgets(STDIN));
$fuelAmount = intval(trim(fgets(STDIN)));
// TODO: أنشئ Car بالـ brand
// TODO: اطبع نتيجة drive() في السطر الأول
// TODO: اطبع نتيجة refuel() مع كمية الوقود في السطر الثاني
?>يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.
جميع دروس البرمجة كائنية التوجه
1أساسيات OOP
الملفات الخارجيةمقدمة في OOPClasses مقابل Objectsالكلمة المفتاحية $thisالـ Methodsالـ Propertiesالمنشئ (__construct)الهادم (__destruct)مراجعة - آلة حاسبة بسيطة4الوراثة
الوراثة الأساسيةالكلمة المفتاحية parent::إعادة تعريف الدوال (Method Overriding)الكلمة المفتاحية finalالأصناف المجردة (Abstract Classes)مراجعة - هيكلية الموظفين7التغليف (Encapsulation)
العام، المحمي، الخاص (Public, Protected, Private)نظرة متعمقة على معدلات الوصولدوال Getters و Settersإخفاء المعلوماتميزة Constructor Promotion (8.0)مراجعة - نظام سجلات الطلاب10مفاهيم البرمجة كائنية التوجه المتقدمة
التركيب مقابل الوراثةحقن التبعيةالأصناف المجهولةالتعدادات (PHP 8.1)الألياف (PHP 8.1)تعمق في استنساخ الكائناتالمولدات والمكررات13أنماط التصميم - الجزء الثاني
نمط الأمرنمط المحولنمط المزيننمط طريقة القالبنمط الحالةنمط التركيبنمط المستودع2مساحات الأسماء والتحميل التلقائي
مقدمة إلى مساحات الأسماءالكلمة المفتاحية useمعيار التحميل التلقائي PSR-4أداة التحميل التلقائي Composerملخص - مشروع منظم5الواجهات والعقود
مقدمة إلى الواجهاتتنفيذ الواجهاتتنفيذ واجهات متعددةالواجهة مقابل الـ Abstract Classالـ Type Hinting مع الواجهاتملخص - حاسبة الأشكال8الميثودات السحرية (Magic Methods)
مقدمة في الميثودات السحرية__toString و __debugInfo__get, __set, __isset, __unset__call و __callStatic__clone واستنساخ الكائنات__serialize و __unserializeمراجعة - Custom Collection3خصائص الكلاس
خصائص الـ Instance مقابل الخصائص الساكنة (Static)الثوابت في الكلاساتالدوال والخصائص الساكنة (Static)الخصائص الخاصة (Private) والمحمية (Protected)خصائص للقراءة فقط (Readonly) في PHP 8.1مراجعة - مدير الحساب البنكي6تعدد الأشكال (Polymorphism)
مراجعة إعادة تعريف الدوال (Method Overriding)تعدد الأشكال عبر الـ Interfacesتلميح النوع (Type Hinting) وأنواع الاتحاد (Union Types)الربط الاستاتيكي المتأخر (Late Static Binding)ملخص - معالج الدفع (Payment Processor)تدرّب بنفسك: مترجم PHP عبر الإنترنت