Menu
Coddy logo textTech

פולימורפיזם באמצעות ממשקים

חלק מהיחידה תכנות מונחה עצמים במסלול ה-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 עם חתימות של שתי מתודות: save($key, $data) ו-retrieve($key). חוזה זה מבטיח שכל מערכת אחסון תוכל לאחסן ולאחזר נתונים, בלי קשר לאופן שבו היא ממומשת פנימית.
  • FileStorage.php: צרו מחלקה FileStorage שמממשת את Storable. כללו את קובץ הממשק. למחלקה צריכה להיות תכונה פרטית $directory שמוגדרת באמצעות הבנאי. ממשו את save($key, $data) כך שתחזיר "Saving '[data]' to file [directory]/[key].txt". ממשו את retrieve($key) כך שתחזיר "Reading from file [directory]/[key].txt".
  • DatabaseStorage.php: צרו מחלקה DatabaseStorage שגם היא מממשת את Storable. כללו את קובץ הממשק. למחלקה צריכה להיות תכונה פרטית $tableName שמוגדרת באמצעות הבנאי. ממשו את save($key, $data) כך שתחזיר "Inserting '[data]' into table [tableName] with key [key]". ממשו את retrieve($key) כך שתחזיר "Selecting from table [tableName] where key = [key]".
  • main.php: כללו את שני קובצי האחסון. צרו פונקציה בשם storeData שמקבלת פרמטר מסוג Storable, מפתח ונתונים. הפונקציה צריכה להחזיר את התוצאה של קריאה ל-save() עם המפתח והנתונים. צרו פונקציה נוספת בשם fetchData שמקבלת פרמטר מסוג Storable ומפתח, ומחזירה את התוצאה של קריאה ל-retrieve().

תקבלו ארבעה קלטים: נתיב של תיקייה, שם של טבלה, מפתח ונתונים לאחסון. צרו FileStorage עם התיקייה ו-DatabaseStorage עם שם הטבלה. השתמשו בפונקציה storeData() כדי לשמור את הנתונים תחילה באמצעות אחסון הקבצים, ולאחר מכן באמצעות אחסון בסיס הנתונים. הדפיסו כל תוצאה בשורה נפרדת. לאחר מכן השתמשו ב-fetchData() כדי לאחזר נתונים מאחסון הקבצים ולהדפיס את התוצאה.

שימו לב ש-storeData() ו-fetchData() פועלות באופן זהה עם שני סוגי האחסון. הן מתייחסות רק לכך שהאובייקט מממש את Storable. למערכת הקבצים ולבסיס הנתונים יש מימושים פנימיים שונים לחלוטין, ובכל זאת אפשר להחליף ביניהם באמצעות חוזה הממשק.

נסו בעצמכם

<?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, מפתח ונתונים
// עליה להחזיר את התוצאה של קריאה ל-save() עם המפתח והנתונים

// TODO: צרו פונקציה fetchData שמקבלת Storable ומפתח
// עליה להחזיר את התוצאה של קריאה ל-retrieve() עם המפתח

// TODO: צרו מופע של FileStorage עם התיקייה

// TODO: צרו מופע של DatabaseStorage עם שם הטבלה

// TODO: השתמשו ב-storeData() כדי לשמור נתונים באחסון קבצים, ואז הדפיסו את התוצאה

// TODO: השתמשו ב-storeData() כדי לשמור נתונים באחסון מסד נתונים, ואז הדפיסו את התוצאה

// TODO: השתמשו ב-fetchData() כדי לאחזר נתונים מאחסון קבצים, ואז הדפיסו את התוצאה
?>
quiz iconבחנו את עצמכם

השיעור הזה כולל חידון קצר. התחילו את השיעור כדי לענות עליו ולעקוב אחרי ההתקדמות.

כל השיעורים ביחידה תכנות מונחה עצמים

תרגלו בעצמכם: קומפיילר PHP אונליין