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

تعدد الأشكال عبر الـ Interfaces

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

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

مع الواجهات، يأتي تعدد الأشكال من العقد نفسه. تضمن أي فئة تقوم بتنفيذ واجهة ما امتلاكها لدوال معينة، بغض النظر عن تسلسلها الهرمي للوراثة:

<?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. لا تشترك هذه الأصناف في صنف أب - فهي غير مرتبطة تماماً باستثناء تنفيذها لنفس الواجهة. هذه هي الميزة الأساسية على تعدد الأشكال القائم على الوراثة: يمكنك تجميع الأصناف غير المرتبطة حسب قدراتها بدلاً من أصلها.

نقطة رئيسية: يتيح تعدد الأشكال القائم على الواجهات (Interface-based polymorphism) استخدام الأصناف (classes) غير المرتبطة بشكل تبادلي بناءً على السلوك المشترك، مما يجعل الكود الخاص بك أكثر مرونة وانفصالاً عن تطبيقات محددة.

challenge icon

التحدي

سهل

لنقم ببناء نظام تخزين يوضح تعدد الأشكال القائم على الواجهات (interface-based polymorphism). ستقوم بإنشاء خلفيات تخزين مختلفة — نظام ملفات وقاعدة بيانات — لا يوجد بينهما شيء مشترك سوى أنهما ينفذان نفس واجهة التخزين. ستعمل وظيفة واحدة مع أي نوع تخزين، مما يوضح كيف تمكن الواجهات تعدد الأشكال بين فئات غير مرتبطة تماماً.

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

  • Storable.php — قم بتعريف واجهة Storable مع توقيعين للدوال: save($key, $data) و retrieve($key). يضمن هذا العقد أن أي نظام تخزين يمكنه تخزين واسترجاع البيانات، بغض النظر عن كيفية تنفيذه داخلياً.
  • FileStorage.php — أنشئ فئة FileStorage التي تنفذ Storable. قم بتضمين ملف الواجهة. يجب أن تحتوي الفئة على خاصية خاصة $directory يتم تعيينها من خلال المنشئ (constructor). قم بتنفيذ save($key, $data) لتعيد "Saving '$data' to file $this->directory/$key.txt". قم بتنفيذ retrieve($key) لتعيد "Reading from file $this->directory/$key.txt".
  • DatabaseStorage.php — أنشئ فئة DatabaseStorage التي تنفذ أيضاً Storable. قم بتضمين ملف الواجهة. يجب أن تحتوي الفئة على خاصية خاصة $tableName يتم تعيينها من خلال المنشئ. قم بتنفيذ save($key, $data) لتعيد "Inserting '$data' into table $this->tableName with key $key". قم بتنفيذ retrieve($key) لتعيد "Selecting from table $this->tableName where key = $key".
  • main.php — قم بتضمين كلا ملفي التخزين. أنشئ دالة تسمى storeData تقبل معامل Storable، ومفتاحاً، وبيانات. يجب أن تعيد الدالة نتيجة استدعاء save() مع المفتاح والبيانات. أنشئ دالة أخرى تسمى fetchData تقبل معامل Storable ومفتاحاً، وتعيد نتيجة استدعاء retrieve().

ستتلقى أربعة مدخلات: مسار دليل، واسم جدول، ومفتاح، وبعض البيانات لتخزينها. أنشئ كائن FileStorage مع الدليل وكائن DatabaseStorage مع اسم الجدول. استخدم دالة storeData() الخاصة بك لحفظ البيانات باستخدام تخزين الملفات أولاً، ثم تخزين قاعدة البيانات. اطبع كل نتيجة في سطر خاص بها. ثم استخدم fetchData() للاسترجاع من تخزين الملفات واطبع تلك النتيجة.

لاحظ كيف تعمل storeData() و fetchData() بشكل متطابق مع كلا نوعي التخزين — فهما يهتمان فقط بأن الكائن ينفذ Storable. يمتلك نظام الملفات وقاعدة البيانات تطبيقات داخلية مختلفة تماماً، ومع ذلك فهما قابلان للتبادل من خلال عقد الواجهة.

ورقة مرجعية

تمكن الواجهات (Interfaces) تعدد الأشكال (polymorphism) من خلال السماح باستخدام الفئات غير المرتبطة بشكل متبادل بناءً على السلوك المشترك بدلاً من تسلسل الوراثة.

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

<?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;
    }
}

function notify(Notifiable $notifier, string $message) {
    return $notifier->send($message);
}

echo notify(new EmailNotifier(), "Hello");
echo notify(new SMSNotifier(), "Hello");

تقبل الدالة notify() أي كائن يقوم بتنفيذ Notifiable، بغض النظر عن تسلسل الفئات. وهذا يجعل الكود أكثر مرونة وفصلاً عن التنفيذات المحددة.

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

جرّب بنفسك

<?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() مع المفتاح والبيانات

// TODO: إنشاء دالة fetchData تقبل Storable ومفتاح (key)
// يجب أن تعيد نتيجة استدعاء retrieve() مع المفتاح

// TODO: إنشاء مثيل (instance) من FileStorage مع الدليل (directory)

// TODO: إنشاء مثيل (instance) من DatabaseStorage مع اسم الجدول

// TODO: استخدام storeData() لحفظ البيانات باستخدام تخزين الملفات، ثم طباعة النتيجة

// TODO: استخدام storeData() لحفظ البيانات باستخدام تخزين قاعدة البيانات، ثم طباعة النتيجة

// TODO: استخدام fetchData() للاسترجاع من تخزين الملفات، ثم طباعة النتيجة
?>
quiz iconاختبر نفسك

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

جميع دروس Object Oriented Programming