تعدد الأشكال عبر الـ Interfaces
جزء من قسم البرمجة كائنية التوجه في رحلة PHP على Coddy. الدرس 34 من 91.
في الدرس السابق، رأيت تعدد الأشكال من خلال الوراثة، حيث تتجاوز الفئات الابنة أساليب الفئات الأب. توفر الواجهات طريقة قوية أخرى لتحقيق تعدد الأشكال، خاصةً عند العمل مع فئات غير مرتبطة تشترك في سلوك مشترك.
مع الواجهات، تنشأ تعددية الأشكال من العقد نفسه. تضمن أي فئة تنفّذ واجهةً ما امتلاكها لأساليب معينة، بغضّ النظر عن تسلسل توريثها:
<?php
interface Notifiable {
public function send(string $message);
}
class EmailNotifier implements Notifiable {
public function send(string $message) {
return "Email: " . $message;
}
}
class SMSNotifier implements Notifiable {
public function send(string $message) {
return "SMS: " . $message;
}
}
class PushNotifier implements Notifiable {
public function send(string $message) {
return "Push: " . $message;
}
}
function notify(Notifiable $notifier, string $message) {
return $notifier->send($message);
}
echo notify(new EmailNotifier(), "Hello") . "\n";
echo notify(new SMSNotifier(), "Hello") . "\n";
echo notify(new PushNotifier(), "Hello");
المخرجات:
Email: Hello
SMS: Hello
Push: Helloتعمل الدالة notify() مع أي كائن من نوع Notifiable. لا تشترك هذه الأصناف في صنف أب — فهي غير مترابطة تمامًا، باستثناء أنها تطبّق الواجهة نفسها. وهذه هي الميزة الأساسية مقارنةً بتعدد الأشكال القائم على الوراثة: إذ يمكنك تجميع أصناف غير مترابطة حسب قدراتها بدلًا من نسبها.
نقطة أساسية: يتيح تعدد الأشكال القائم على الواجهات استخدام الأصناف غير المترابطة بالتبادل استنادًا إلى السلوك المشترك، مما يجعل شيفرتك أكثر مرونة وأقل اقترانًا بالتنفيذات المحددة.
التحدي
سهللنُنشئ نظام تخزين يوضّح تعدد الأشكال القائم على الواجهات. ستنشئ أنظمة تخزين خلفية مختلفة، نظام ملفات وقاعدة بيانات، لا يجمع بينهما شيء باستثناء أنهما يطبّقان واجهة التخزين نفسها. ستعمل دالة واحدة مع أي نوع من أنواع التخزين، موضّحةً كيف تتيح الواجهات تعدد الأشكال بين أصناف غير مترابطة تمامًا.
ستنظّم التعليمات البرمجية عبر أربعة ملفات:
Storable.php: عرّف واجهةStorableبتوقيعَي method:save($key, $data)وretrieve($key). يضمن هذا العقد قدرة أي نظام تخزين على تخزين data واسترجاعها، بغض النظر عن كيفية Implement ذلك داخليًا.FileStorage.php: أنشئ class باسمFileStorageيطبّقStorable. أدرج ملف الواجهة. يجب أن يحتوي class على property خاصة باسم$directoryيتم تعيينها من خلال constructor. Implementsave($key, $data)لإرجاع"Saving '[data]' to file [directory]/[key].txt". Implementretrieve($key)لإرجاع"Reading from file [directory]/[key].txt".DatabaseStorage.php: أنشئ class باسمDatabaseStorageيطبّق أيضًاStorable. أدرج ملف الواجهة. يجب أن يحتوي class على property خاصة باسم$tableNameيتم تعيينها من خلال constructor. Implementsave($key, $data)لإرجاع"Inserting '[data]' into table [tableName] with key [key]". Implementretrieve($key)لإرجاع"Selecting from table [tableName] where key = [key]".main.php: أدرج ملفَي التخزين. أنشئ function باسمstoreDataتقبل وسيطًا من النوعStorable، وkey، وdata. يجب أن تُرجع function نتيجة callingsave()باستخدام key وdata. أنشئ function أخرى باسمfetchDataتقبل وسيطًا من النوعStorableوkey، وتُرجع نتيجة callingretrieve().
ستتلقى أربعة inputs: مسار directory، واسم table، وkey، وبعض data لتخزينها. أنشئ FileStorage باستخدام directory، وأنشئ DatabaseStorage باستخدام اسم table. استخدم function storeData() لحفظ data باستخدام تخزين الملفات أولًا، ثم تخزين قاعدة البيانات. اطبع كل result في سطر مستقل. بعد ذلك استخدم fetchData() للاسترجاع من تخزين الملفات واطبع ذلك result.
لاحظ كيف تعمل storeData() وfetchData() بالطريقة نفسها مع كلا نوعَي التخزين. كل ما يهمهما هو أن object يطبّق Storable. يملك نظام الملفات وقاعدة البيانات Implement داخلية مختلفة تمامًا، ومع ذلك فهما قابلان للتبديل من خلال عقد الواجهة.
جرّب بنفسك
<?php
require_once 'FileStorage.php';
require_once 'DatabaseStorage.php';
// اقرأ المدخلات
$directory = trim(fgets(STDIN));
$tableName = trim(fgets(STDIN));
$key = trim(fgets(STDIN));
$data = trim(fgets(STDIN));
// TODO: أنشئ دالة storeData تقبل Storable و key و data
// يجب أن تُرجع نتيجة استدعاء save() مع الـ key والـ data
// TODO: أنشئ دالة fetchData تقبل Storable و key
// يجب أن تُرجع نتيجة استدعاء retrieve() مع الـ key
// TODO: أنشئ مثيلاً من FileStorage مع الـ directory
// TODO: أنشئ مثيلاً من DatabaseStorage مع اسم الجدول
// TODO: استخدم storeData() لحفظ البيانات بتخزين الملفات، ثم اطبع النتيجة
// TODO: استخدم storeData() لحفظ البيانات بتخزين قاعدة البيانات، ثم اطبع النتيجة
// TODO: Use fetchData() to retrieve from file storage, then print the result
?>يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.
جميع دروس البرمجة كائنية التوجه
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 عبر الإنترنت