Menu
Coddy logo textTech

איטרציה פולימורפית

חלק מהיחידה תכנות מונחה עצמים במסלול ה-C של Coddy. שיעור 42 מתוך 61.

למדת ליצור ממשקים ולממש אותם באמצעות פונקציות קונקרטיות שונות. היתרון האמיתי מגיע כשאפשר להתייחס לאוסף של אובייקטים שונים באופן אחיד: לעבור עליהם ולקרוא לכל אחד מהם לאותו מצביע פונקציה, בלי קשר להתנהגות הספציפית שמחוברת אליו.

דמיינו שיש לכם מערך של מבני Action, שלכל אחד מהם מוקצית פונקציית execute משלו:

typedef void (*ExecuteFunc)(void);

typedef struct {
    const char* name;
    ExecuteFunc execute;
} Action;

void jump(void) { printf("Jumping!\n"); }
void run(void)  { printf("Running!\n"); }
void rest(void) { printf("Resting...\n"); }

אפשר ליצור מערך של הפעולות האלה, שכל אחת מהן מחוברת לפונקציה אחרת, ואז לעבור עליהן באמצעות לולאת for פשוטה:

int main() {
    Action actions[3] = {
        { "Jump", jump },
        { "Run", run },
        { "Rest", rest }
    };
    
    for (int i = 0; i < 3; i++) {
        printf("%s: ", actions[i].name);
        actions[i].execute();
    }
    return 0;
}

הלולאה לא יודעת ולא אכפת לה מה כל פעולה עושה באופן פנימי. היא פשוט קוראת ל־execute() בכל איבר.

זוהי פולימורפיות בפעולה: אותו קוד מטפל באובייקטים בעלי התנהגויות שונות לחלוטין. הוספת סוג פעולה חדש אינה דורשת שינויים בלוגיקת האיטרציה; פשוט מוסיפים עוד איבר למערך.

challenge icon

אתגר

קל

בואו נבנה מערכת לניהול Task שמדגימה איטרציה פולימורפית: עיבוד אוסף של אובייקטים שחולקים ממשק משותף אך מבצעים התנהגויות שונות.

תארגנו את הקוד שלכם בשלושה קבצים:

  • task.h: הגדירו כאן את ממשק המשימות. צרו טיפוס מצביע לפונקציה בשם TaskFunc שאינו מקבל פרמטרים ואינו מחזיר ערך. לאחר מכן הגדירו מבנה Task שמכיל name (מטיפוס const char*) ומצביע לפונקציה בשם run מטיפוס TaskFunc.
  • task.c: ממשו שלוש פונקציות משימה שונות שמייצגות סוגי עבודה שונים:
    • backup_task: מדפיסה Backing up data...
    • cleanup_task: מדפיסה Cleaning up files...
    • report_task: מדפיסה Generating report...
  • main.c: חברו כאן את הכול. צרו מערך של מבני Task, שכל אחד מהם מקושר לפונקציית משימה אחרת. לאחר מכן עברו על המערך ובצעו כל משימה באופן פולימורפי: עבור כל משימה, הדפיסו את שמה ולאחר מכן קראו לפונקציית run שלה.

התוכנית שלכם תקבל קלט יחיד: מספר המשימות שיש להריץ (1, 2 או 3).

צרו מערך שמכיל בדיוק שלוש משימות בסדר הבא: משימת גיבוי בשם Backup, משימת ניקוי בשם Cleanup ומשימת הפקת דוח בשם Report. בהתאם למספר שהתקבל בקלט, עברו רק על מספר המשימות הזה מתחילת המערך.

עבור כל משימה במהלך האיטרציה, הדפיסו את שם המשימה ואחריו נקודתיים ורווח, ואז קראו לפונקציית run שלה.

פלט לדוגמה כאשר הקלט הוא 2:

Backup: Backing up data...
Cleanup: Cleaning up files...

פלט לדוגמה כאשר הקלט הוא 3:

Backup: Backing up data...
Cleanup: Cleaning up files...
Report: Generating report...

התובנה המרכזית היא שהלולאה שלכם לא צריכה לדעת מה כל משימה עושה. היא פשוט מדפיסה את השם וקוראת ל־run() בכל איבר. אותו קוד איטרציה מטפל באופן אחיד בכל סוגי המשימות. זכרו להשתמש בשומרי הכללה בקובץ הכותרת שלכם.

נסו בעצמכם

#include <stdio.h>
#include "task.h"

// הכריזו על פונקציות המשימה שממומשות ב-task.c
void backup_task(void);
void cleanup_task(void);
void report_task(void);

int main() {
    int count;
    scanf("%d", &count);
    
    // TODO: צרו מערך של 3 מבני Task בסדר הבא:
    // 1. משימת גיבוי בשם "Backup" שמשתמשת ב-backup_task
    // 2. משימת ניקוי בשם "Cleanup" שמשתמשת ב-cleanup_task
    // 3. משימת דוח בשם "Report" שמשתמשת ב-report_task
    
    // TODO: עברו על 'count' משימות מתוך המערך
    // עבור כל משימה, הדפיסו: "<name>: " ואז קראו לפונקציה run שלה
    
    return 0;
}
quiz iconבחנו את עצמכם

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

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

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