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

تنفيذ الـ Interfaces

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

الآن بعد أن فهمت ما هي الـ interface — وهي عبارة عن struct يحتوي فقط على function pointers — فقد حان الوقت لإنشاء تطبيقات ملموسة (concrete implementations) تفي بهذا العقد.

تذكر واجهة ILogger الخاصة بنا من الدرس السابق:

typedef void (*LogFunc)(const char* message);

typedef struct {
    LogFunc log;
} ILogger;

لتنفيذ هذه الواجهة، نقوم بكتابة دوال فعلية تطابق توقيع LogFunc، ثم نقوم بإنشاء مثيلات ILogger مع تعيين تلك الدوال لها:

void console_log(const char* message) {
    printf("[CONSOLE] %s\n", message);
}

void file_log(const char* message) {
    printf("[FILE] %s\n", message);  // محاكاة الإخراج إلى ملف
}

ILogger create_console_logger() {
    ILogger logger = { console_log };
    return logger;
}

ILogger create_file_logger() {
    ILogger logger = { file_log };
    return logger;
}

تعيد كل دالة "constructor" كائن ILogger مع دالة مختلفة مرتبطة بها. لا يحتاج الكود الذي يستخدم أدوات التسجيل (loggers) هذه إلى معرفة أي تنفيذ (implementation) تلقاه:

void do_work(ILogger* logger) {
    logger->log("Starting work...");
    logger->log("Work complete!");
}

int main() {
    ILogger console = create_console_logger();
    ILogger file = create_file_logger();
    
    do_work(&console);  // يستخدم console_log
    do_work(&file);     // يستخدم file_log
    return 0;
}

إن الدالة do_work منفصلة تماماً عن آلية التسجيل. يمكنك تبديل المسجلات بحرية دون تغيير أي من المنطق الأساسي — هذه هي قوة البرمجة لواجهة برمجية.

challenge icon

التحدي

سهل

لنقم ببناء نظام INotifier يوضح كيف يمكن للتطبيقات المختلفة أن تفي بنفس عقد الواجهة (interface contract). ستقوم بإنشاء أدوات إشعار ملموسة — EmailNotifier و SMSNotifier — كلاهما يتوافق مع واجهة مشتركة ولكنهما ينتجان مخرجات مختلفة.

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

  • notifier.h: قم بتعريف الواجهة هنا. أنشئ نوع مؤشر دالة يسمى NotifyFunc يأخذ رسالة من نوع const char* ولا يعيد شيئاً. ثم قم بتعريف هيكل INotifier يحتوي فقط على مؤشر دالة notify من هذا النوع. قم أيضاً بالتصريح عن دالتي بناء (constructor functions): create_email_notifier و create_sms_notifier، حيث تعيد كل منهما INotifier بالقيمة.
  • notifier.c: قم بتنفيذ دوال الإشعار الملموسة ودوال البناء هنا. أنشئ:
    • email_notify — يطبع الرسالة بالتنسيق: [EMAIL] message
    • sms_notify — يطبع الرسالة بالتنسيق: [SMS] message
    • create_email_notifier — يعيد INotifier مرتبطة بـ email_notify
    • create_sms_notifier — يعيد INotifier مرتبطة بـ sms_notify
  • main.c: اجمع كل شيء معاً هنا. اقرأ نوع الإشعار والرسالة، وأنشئ أداة الإشعار المناسبة باستخدام دوال البناء، وأرسل الإشعار من خلال الواجهة.

سيتلقى برنامجك مدخلين: نوع الإشعار (1 للبريد الإلكتروني، 2 للرسائل القصيرة SMS) و رسالة لإرسالها.

استخدم دوال البناء لإنشاء أداة الإشعار المناسبة، ثم استدعِ notify من خلال الواجهة لعرض الرسالة.

مثال على المخرجات عندما يكون النوع 1 والرسالة هي Meeting at 3pm:

[EMAIL] Meeting at 3pm

مثال على المخرجات عندما يكون النوع 2 والرسالة هي Your code shipped:

[SMS] Your code shipped

تكمن قوة هذا النمط في أن main.c لا يحتاج إلى معرفة كيفية عمل كل أداة إشعار داخلياً — فهو ببساطة يستدعي دالة notify الخاصة بالواجهة، ويتم تنفيذ التطبيق الصحيح. تذكر استخدام حراس التضمين (include guards) في ملف الرأس الخاص بك.

ورقة مرجعية

لتنفيذ واجهة (interface) في لغة C، قم بإنشاء دوال ملموسة (concrete functions) تطابق توقيع مؤشر الدالة، ثم قم بتعيينها لمثيلات الواجهة.

قم بتعريف الواجهة باستخدام مؤشرات الدوال:

typedef void (*LogFunc)(const char* message);

typedef struct {
    LogFunc log;
} ILogger;

قم بإنشاء التنفيذات الملموسة:

void console_log(const char* message) {
    printf("[CONSOLE] %s\n", message);
}

void file_log(const char* message) {
    printf("[FILE] %s\n", message);
}

استخدم دوال البناء (constructor functions) لإنشاء مثيلات الواجهة:

ILogger create_console_logger() {
    ILogger logger = { console_log };
    return logger;
}

ILogger create_file_logger() {
    ILogger logger = { file_log };
    return logger;
}

الكود الذي يستخدم الواجهة يكون منفصلاً عن التنفيذات المحددة:

void do_work(ILogger* logger) {
    logger->log("Starting work...");
}

int main() {
    ILogger console = create_console_logger();
    ILogger file = create_file_logger();
    
    do_work(&console);  // يستخدم console_log
    do_work(&file);     // يستخدم file_log
    return 0;
}

جرّب بنفسك

#include <stdio.h>
#include "notifier.h"

int main() {
    int type;
    char message[256];
    
    // قراءة نوع الإشعار (1 للبريد الإلكتروني، 2 للرسائل القصيرة SMS)
    scanf("%d", &type);
    getchar(); // استهلاك السطر الجديد
    
    // قراءة الرسالة
    fgets(message, sizeof(message), stdin);
    // إزالة السطر الجديد في النهاية إذا وجد
    int len = 0;
    while (message[len] != '\0') len++;
    if (len > 0 && message[len-1] == '\n') message[len-1] = '\0';
    
    // TODO: إنشاء المُخطر (notifier) المناسب بناءً على النوع
    // استخدم create_email_notifier() للنوع 1
    // استخدم create_sms_notifier() للنوع 2
    
    // TODO: استدعاء دالة notify من خلال الواجهة
    // notifier.notify(message);
    
    return 0;
}
quiz iconاختبر نفسك

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

جميع دروس Object Oriented Programming