التكرار متعدد الأشكال
جزء من قسم البرمجة كائنية التوجه في رحلة C على Coddy. الدرس 42 من 61.
لقد تعلّمت إنشاء الواجهات وImplementها باستخدام functions ملموسة مختلفة. وتظهر الفائدة الحقيقية عندما تتمكّن من التعامل مع مجموعة من الكائنات المختلفة بشكل موحّد: Iterate خلالها واستدعاء pointer function نفسه على كلٍّ منها، بغضّ النظر عن السلوك المحدّد المرتبط بها.
تخيّل أن لديك مصفوفة من هياكل 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;
}لا تعرف الحلقة ولا تهتم بما يفعله كل Action داخليًا. إنها تستدعي ببساطة execute() على كل عنصر.
هذا هو تعدد الأشكال قيد التنفيذ: يتعامل الكود نفسه مع كائنات ذات سلوكيات مختلفة تمامًا. لا يتطلب إضافة نوع إجراء جديد أي تغييرات في منطق التكرار؛ ما عليك سوى إضافة عنصر آخر إلى array.
التحدي
سهللنبنِ نظامًا لإدارة Task يوضّح التكرار متعدد الأشكال: معالجة مجموعة من الكائنات التي تشترك في واجهة common، لكنها تنفّذ سلوكيات مختلفة.
ستنظّم شفرتك عبر ثلاثة ملفات:
task.h: Define واجهة المهمة هنا. Create نوع مؤشر function يُسمّىTaskFunc، ولا يأخذ parameters ويُرجع nothing. ثم Define بنيةTaskcontainingname(وهوconst char*) ومؤشر function runمن النوعTaskFunc.task.c: Implement ثلاث functions مختلفة للمهام، تمثّل أنواعًا مختلفة من العمل:backup_task: تطبعBacking up data...cleanup_task: تطبعCleaning up files...report_task: تطبعGenerating report...
main.c: اجمع كل شيء هنا. Create array من بنياتTask، تكون كل واحدة منها مرتبطة بـ function مهمة مختلفة. ثم Iterate عبر array وexecute كل مهمة polymorphically؛ وبالنسبة إلى كل مهمة، اطبع اسمها ثم استدعِ function runالخاصة بها.
سيتلقى برنامجك إدخالًا واحدًا: عدد المهام المطلوب تشغيلها (1 أو 2 أو 3).
Create array containing ثلاث مهام بالضبط بهذا order: مهمة نسخ احتياطي تُسمّى Backup، ومهمة تنظيف تُسمّى Cleanup، ومهمة تقرير تُسمّى Report. بناءً على count المُدخل، Iterate عبر هذا العدد فقط من المهام بدءًا من بداية array.
لكل مهمة في عملية التكرار، اطبع اسم المهمة متبوعًا بنقطتين ومسافة، ثم استدعِ function 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 في ملف الترويسة الخاص بك.
جرّب بنفسك
#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;
}
يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.
جميع دروس البرمجة كائنية التوجه
1أساسيات البرمجة التركيبية
ملفات الترويسةحواجز التضمينملفات المصدرالدوال الساكنةمراجعة: آلة حاسبة تركيبية4التغليف (Encapsulation)
مفهوم الـ Opaque Pointersتعريف الـ Opaque Structsالـ Getters والـ Settersالتحقق من البيانات في الـ Settersملخص: الصندوق السري2الكائنات والأساليب
الـ Structs ككائناتمؤشر 'Self'صحة استخدام Constالمؤشر مقابل القيمةالأساليب المساعدةملخص: Point Manager5مشروع: حساب بنكي بسيط
إعداد المشروعتنفيذ الحساب8تعدد الأشكال (Polymorphism)
مؤشرات الدوال في الـ Structsمحاكاة الـ Methodsمفهوم الـ Interfaceتنفيذ الـ Interfacesالتكرار متعدد الأشكالمراجعة: Greeter3دورة حياة الكائن
نمط المنشئ (Constructor Pattern)نمط الهادم (Destructor Pattern)تهيئة الـ Stackالنسخ العميق (Deep Copy)مراجعة: String Wrapper6الوراثة عبر التركيب
تضمين الـ Structقاعدة العضو الأولالوصول إلى أعضاء الأبعملية الـ Upcastingمراجعة: هرمية الأشكال9مشروع: رسّام الأشكال
نظرة عامة على المشروعتنفيذ الدائرةتنفيذ المستطيلاستخدام تعدد الأشكالحاوية الأشكالتدرّب بنفسك: مترجم C عبر الإنترنت