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

التكرار متعدد الأشكال

جزء من قسم Object Oriented Programming في رحلة C على Coddy — الدرس 42 من 61.

لقد تعلمت كيفية إنشاء الواجهات (interfaces) وتنفيذها باستخدام دوال ملموسة (concrete functions) مختلفة. وتأتي الفائدة الحقيقية عندما يمكنك التعامل مع مجموعة من الكائنات المختلفة بشكل موحد — عبر التكرار خلالها واستدعاء نفس مؤشر الدالة (function pointer) لكل منها، بغض النظر عن السلوك المحدد المبرمج بداخلها.

تخيل أن لديك مصفوفة من هياكل 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() لكل عنصر.

هذا هو تعدد الأشكال (polymorphism) قيد التنفيذ — حيث يتعامل نفس الكود مع كائنات ذات سلوكيات مختلفة تمامًا. لا يتطلب إضافة نوع إجراء جديد أي تغييرات في منطق التكرار؛ فكل ما عليك فعله هو إضافة عنصر آخر إلى المصفوفة.

challenge icon

التحدي

سهل

لنقم ببناء نظام إدارة Task يوضح التكرار متعدد الأشكال (polymorphic iteration) — معالجة مجموعة من الكائنات التي تشترك في واجهة برمجية واحدة ولكنها تنفذ سلوكيات مختلفة.

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

  • 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 guards) في ملف الرأس الخاص بك.

ورقة مرجعية

يمكنك التكرار عبر مجموعات من الكائنات التي تشترك في واجهة برمجية مشتركة، واستدعاء نفس مؤشر الدالة على كل عنصر بغض النظر عن تنفيذه المحدد. هذا هو تعدد الأشكال (polymorphism) — معاملة كائنات مختلفة بشكل موحد.

قم بتعريف نوع مؤشر دالة وهيكل (struct) يستخدمه:

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"); }

قم ببناء مصفوفة من الهياكل، كل منها مرتبط بدالة مختلفة، ثم قم بالتكرار عبرها:

Action actions[3] = {
    { "Jump", jump },
    { "Run", run },
    { "Rest", rest }
};

for (int i = 0; i < 3; i++) {
    printf("%s: ", actions[i].name);
    actions[i].execute();
}

لا تحتاج الحلقة إلى معرفة ما يفعله كل إجراء داخلياً — فهي ببساطة تستدعي مؤشر الدالة في كل عنصر. إضافة أنواع جديدة لا تتطلب أي تغييرات في منطق التكرار.

جرّب بنفسك

#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اختبر نفسك

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

جميع دروس Object Oriented Programming