تنفيذ الـ 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 منفصلة تماماً عن آلية التسجيل. يمكنك تبديل المسجلات بحرية دون تغيير أي من المنطق الأساسي — هذه هي قوة البرمجة لواجهة برمجية.
التحدي
سهللنقم ببناء نظام 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] messagesms_notify— يطبع الرسالة بالتنسيق:[SMS] messagecreate_email_notifier— يعيدINotifierمرتبطة بـemail_notifycreate_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;
}
يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.
جميع دروس 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مشروع: رسّام الأشكال
نظرة عامة على المشروعتنفيذ الدائرة