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

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

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

challenge icon

التحدي

سهل

لنُنشئ نظام تخزين يوضّح تعدد الأشكال القائم على الواجهات. ستنشئ أنظمة تخزين خلفية مختلفة، نظام ملفات وقاعدة بيانات، لا يجمع بينهما شيء باستثناء أنهما يطبّقان واجهة التخزين نفسها. ستعمل دالة واحدة مع أي نوع من أنواع التخزين، موضّحةً كيف تتيح الواجهات تعدد الأشكال بين أصناف غير مترابطة تمامًا.

ستنظّم التعليمات البرمجية عبر أربعة ملفات:

  • Storable.php: عرّف واجهة Storable بتوقيعَي method: save($key, $data) وretrieve($key). يضمن هذا العقد قدرة أي نظام تخزين على تخزين data واسترجاعها، بغض النظر عن كيفية Implement ذلك داخليًا.
  • FileStorage.php: أنشئ class باسم FileStorage يطبّق Storable. أدرج ملف الواجهة. يجب أن يحتوي class على property خاصة باسم $directory يتم تعيينها من خلال constructor. Implement save($key, $data) لإرجاع "Saving '[data]' to file [directory]/[key].txt". Implement retrieve($key) لإرجاع "Reading from file [directory]/[key].txt".
  • DatabaseStorage.php: أنشئ class باسم DatabaseStorage يطبّق أيضًا Storable. أدرج ملف الواجهة. يجب أن يحتوي class على property خاصة باسم $tableName يتم تعيينها من خلال constructor. Implement save($key, $data) لإرجاع "Inserting '[data]' into table [tableName] with key [key]". Implement retrieve($key) لإرجاع "Selecting from table [tableName] where key = [key]".
  • main.php: أدرج ملفَي التخزين. أنشئ function باسم storeData تقبل وسيطًا من النوع Storable، وkey، وdata. يجب أن تُرجع function نتيجة calling save() باستخدام key وdata. أنشئ function أخرى باسم fetchData تقبل وسيطًا من النوع Storable وkey، وتُرجع نتيجة calling retrieve().

ستتلقى أربعة 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
?>
quiz iconاختبر نفسك

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

جميع دروس البرمجة كائنية التوجه

تدرّب بنفسك: مترجم PHP عبر الإنترنت