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

محاكاة الـ 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).

يعكس هذا النمط كيفية عمل الكائنات في اللغات الأخرى: يمكن أن يكون لكل كائن تنفيذه الخاص للدالة، على الرغم من أنها تشترك في الواجهة نفسها.

challenge icon

التحدي

سهل

لنقم ببناء نظام 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;
}
quiz iconاختبر نفسك

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

جميع دروس Object Oriented Programming