איטרציה פולימורפית
חלק מהיחידה תכנות מונחה עצמים במסלול ה-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() בכל איבר.
זוהי פולימורפיות בפעולה: אותו קוד מטפל באובייקטים בעלי התנהגויות שונות לחלוטין. הוספת סוג פעולה חדש אינה דורשת שינויים בלוגיקת האיטרציה; פשוט מוסיפים עוד איבר למערך.
אתגר
קלבואו נבנה מערכת לניהול 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;
}
השיעור הזה כולל חידון קצר. התחילו את השיעור כדי לענות עליו ולעקוב אחרי ההתקדמות.
כל השיעורים ביחידה תכנות מונחה עצמים
4כימוס
המושג של מצביעים אטומיםהגדרת מבנים אטומיםפונקציות Get ו-Setאימות ב-Settersסיכום: הקופסה הסודית2אובייקטים ומתודות
מבנים כאובייקטיםמצביע 'Self'נכונות constמצביע לעומת ערךמתודות עזרחזרה: מנהל נקודות5פרויקט: חשבון בנק פשוט
הגדרת הפרויקטמימוש החשבוןתרגלו בעצמכם: קומפיילר C אונליין