محاكاة الـ Methods
جزء من قسم Object Oriented Programming في رحلة C على Coddy — الدرس 39 من 61.
لقد رأيت كيف تضع مؤشر دالة (function pointer) داخل struct. تكمن القوة الحقيقية عندما تقوم بتعيين دوال مختلفة لمثيلات مختلفة من نفس نوع الـ struct — هذه هي الطريقة التي نحاكي بها الـ methods ذات السلوك المتغير.
ضع في اعتبارك بنية (struct) تمثل إجراءً بمنطق قابل للتخصيص:
typedef void (*ActionFunc)(void);
typedef struct {
const char* name;
ActionFunc execute;
} Action;عند إنشاء المثيلات (instances)، تقوم بربط دوال مختلفة بكل منها:
void say_hello(void) {
printf("Hello!\n");
}
void say_goodbye(void) {
printf("Goodbye!\n");
}
int main() {
Action greet = { "Greet", say_hello };
Action farewell = { "Farewell", say_goodbye };
greet.execute(); // المخرجات: Hello!
farewell.execute(); // المخرجات: Goodbye!
return 0;
}كلاً من greet و farewell هما من نفس النوع (Action)، ومع ذلك فإن استدعاء execute على كل منهما ينتج نتائج مختلفة تماماً. لا يقتصر الـ struct على تخزين البيانات فحسب - بل يحمل سلوكاً يتغير لكل مثيل (instance).
يعكس هذا النمط كيفية عمل الكائنات في اللغات الأخرى: يمكن أن يكون لكل كائن تنفيذه الخاص للدالة، على الرغم من أنها تشترك في الواجهة نفسها.
التحدي
سهللنقم ببناء نظام Speaker يوضح كيف يمكن لمثيلات مختلفة من نفس نوع الـ struct أن تظهر سلوكيات مختلفة تماماً بناءً على الوظيفة (function) الموصولة بها.
ستقوم بإنشاء ملفين لتنظيم الكود الخاص بك:
speaker.h: قم بتعريف نوع مؤشر وظيفة (function pointer) يسمىSpeakFuncلا يأخذ أي معاملات ولا يعيد شيئاً. ثم قم بتعريف struct باسمSpeakerيحتوي علىname(من نوعconst char*) ومؤشر وظيفةspeakمن نوعSpeakFunc.main.c: قم بتضمين ملف الرأس (header) الخاص بك وقم بتنفيذ ثلاث وظائف تحدث مختلفة:bark— يطبعWoof!meow— يطبعMeow!chirp— يطبعChirp!
Speaker، كل منهما موصول بوظيفة مختلفة بناءً على المدخلات، واستدعِ طرقspeakالخاصة بهما لرؤية السلوك متعدد الأشكال (polymorphic behavior) قيد التنفيذ.
سيتلقى برنامجك مدخلين: نوع المتحدث الأول (1 لـ bark، و 2 لـ meow، و 3 لـ chirp) و نوع المتحدث الثاني (أيضاً 1، 2، أو 3).
قم بإنشاء مثيلين من Speaker — يمكنك تسميتهما بأي اسم تريده (مثل "Dog"، "Cat"، "Bird"، أو ببساطة "Speaker1"، "Speaker2"). قم بتوصيل كل منهما بالوظيفة المناسبة بناءً على رمز الإدخال الخاص به، ثم استدعِ speak على كل مثيل بالترتيب.
مثال على المخرجات عندما يكون النوع الأول 1 والنوع الثاني 2:
Woof!
Meow!مثال على المخرجات عندما يكون النوع الأول 3 والنوع الثاني 3:
Chirp!
Chirp!الفكرة الأساسية هنا هي أن كلا المتحدثين هما من نفس النوع Speaker، ومع ذلك فإنهما ينتجان مخرجات مختلفة لأن لكل منهما وظيفة مختلفة مخصصة لعضو speak الخاص به. تذكر استخدام حراس التضمين (include guards) في ملف الرأس الخاص بك.
ورقة مرجعية
يمكن لمثيلات مختلفة من نفس نوع الـ struct أن تحمل مؤشرات دوال مختلفة، مما يتيح سلوكاً متعدد الأشكال (polymorphic).
عرّف نوع مؤشر دالة و struct يستخدمه:
typedef void (*ActionFunc)(void);
typedef struct {
const char* name;
ActionFunc execute;
} Action;أنشئ مثيلات مع تعيين دوال مختلفة لها:
void say_hello(void) {
printf("Hello!\n");
}
void say_goodbye(void) {
printf("Goodbye!\n");
}
int main() {
Action greet = { "Greet", say_hello };
Action farewell = { "Farewell", say_goodbye };
greet.execute(); // Output: Hello!
farewell.execute(); // Output: Goodbye!
return 0;
}يتشارك كلا المثيلين نفس النوع ولكنهما يظهران سلوكاً مختلفاً عند استدعاء مؤشرات الدوال الخاصة بهما.
جرّب بنفسك
#include <stdio.h>
#include "speaker.h"
// TODO: قم بتنفيذ دالة bark التي تطبع "Woof!"
// TODO: قم بتنفيذ دالة meow التي تطبع "Meow!"
// TODO: قم بتنفيذ دالة chirp التي تطبع "Chirp!"
int main() {
int type1, type2;
scanf("%d", &type1);
scanf("%d", &type2);
// TODO: قم بإنشاء مثيلين (instances) من Speaker
// TODO: قم بربط دالة speak للمكبر الأول بناءً على type1
// (1 = bark، 2 = meow، 3 = chirp)
// TODO: قم بربط دالة speak للمكبر الثاني بناءً على type2
// (1 = bark، 2 = meow، 3 = chirp)
// TODO: قم باستدعاء speak لكل مكبر بالترتيب
return 0;
}
يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.
جميع دروس Object Oriented Programming
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مشروع: رسّام الأشكال
نظرة عامة على المشروعتنفيذ الدائرة