الـ Getters والـ Setters
جزء من قسم Object Oriented Programming في رحلة C على Coddy — الدرس 19 من 61.
مع المؤشرات غير الشفافة (opaque pointers)، تكون أعضاء الـ struct مخفية عن الكود الخارجي. ولكن إذا لم يتمكن الكود الخارجي من الوصول إلى البيانات مباشرة، فكيف نقوم بقراءتها أو تعديلها؟ الإجابة هي دوال الـ getter والـ setter — وهي واجهات عامة توفر وصولاً متحكماً فيه للبيانات الخاصة.
يقوم الـ getter بإرجاع قيمة عضو مخفي:
// counter.h
int counter_get_value(const Counter *c);
// counter.c
int counter_get_value(const Counter *c) {
return c->value;
}
يقوم الـ setter بتعديل عضو مخفي:
// counter.h
void counter_set_value(Counter *c, int new_value);
// counter.c
void counter_set_value(Counter *c, int new_value) {
c->value = new_value;
}
لاحظ اصطلاح التسمية: أضف اسم النوع كبادئة للوظائف (counter_) لتجنب التعارضات وجعل واجهة برمجة التطبيقات (API) واضحة. تستخدم دوال الجلب (Getters) مؤشرات const لأنها لا تعدل الكائن.
الآن يمكن للكود الخارجي التفاعل مع البيانات المخفية بأمان:
// main.c
Counter *c = create_counter(10);
printf("%d\n", counter_get_value(c)); // 10
counter_set_value(c, 25);
printf("%d\n", counter_get_value(c)); // 25
يوفر لك هذا النمط تحكماً كاملاً في كيفية الوصول إلى البيانات — وهو أساس سنبني عليه في الدرس القادم عندما نضيف منطق التحقق (validation logic) إلى الـ setters.
التحدي
سهللنقم ببناء وحدة Score تستخدم نمط المؤشر غير الشفاف (opaque pointer pattern) مع دوال الجلب (getter) والتعيين (setter). ستقوم بإنشاء متتبع نقاط مخفي حيث لا يمكن للكود الخارجي التفاعل مع البيانات إلا من خلال الواجهة العامة التي توفرها.
ستقوم بإنشاء ثلاثة ملفات:
score.h: واجهتك العامة. قم بالتصريح عن نوعScoreغير شفاف باستخدام تصريح مسبق (forward declaration) (بدون جسم الـ struct!). قم بالتصريح عن هذه الدوال:create_score— تأخذ عدداً صحيحاً للنقاط الأولية وتُرجع مؤشراً من نوع Scorefree_score— تأخذ مؤشراً من نوع Score وتقوم بتحرير الذاكرةscore_get_points— دالة جلب (getter) تأخذconst Score *وتُرجع النقاط الحاليةscore_set_points— دالة تعيين (setter) تأخذScore *وعدداً صحيحاً، وتقوم بتحديث قيمة النقاط
SCORE_H.score.c: قم بتعريف الـstruct Scoreالفعلي مع عضو مخفي واحد:int points. قم بتنفيذ الدوال الأربع جميعها. يجب أن تُرجع دالة الجلب القيمة المخفية، ويجب أن تقوم دالة التعيين بتحديثها. تذكر أن دوال الجلب تستخدم مؤشراتconstلأنها لا تعدل الكائن.main.c: قم باستعراض وحدتك من خلال إنشاء نتيجة (score)، وقراءة قيمتها، وتعديلها من خلال دالة التعيين، وقراءتها مرة أخرى.
ستتلقى مدخلين: النقاط الأولية (عدد صحيح) و قيمة النقاط الجديدة (عدد صحيح) لتعيينها.
في ملفك الرئيسي، قم بإنشاء Score بالنقاط الأولية، واطبع القيمة الحالية باستخدام دالة الجلب، ثم قم بتحديثها باستخدام دالة التعيين بالقيمة الجديدة، واطبع القيمة المحدثة، ثم قم بتحرير الـ score واطبع تأكيداً.
اطبع المخرجات بهذا التنسيق:
Initial: {points}
Updated: {points}
Freedعلى سبيل المثال، مع المدخلات 100 و 250، ستكون المخرجات:
Initial: 100
Updated: 250
Freedلاحظ كيف لا يمكن لـ main.c الوصول إلى points مباشرة — يجب أن يمر عبر دوال الجلب والتعيين الخاصة بك. هذا هو التغليف (encapsulation) في أبهى صوره: أنت تتحكم بالضبط في كيفية قراءة البيانات المخفية وتعديلها.
ورقة مرجعية
مع المؤشرات غير الشفافة (opaque pointers)، يتم إخفاء أعضاء الهيكل (struct members) عن الكود الخارجي. للوصول إلى هذه البيانات الخاصة أو تعديلها، استخدم دوال getter و setter.
تعيد دالة الـ getter قيمة العضو المخفي وتستخدم مؤشر const لأنها لا تعدل الكائن:
// counter.h
int counter_get_value(const Counter *c);
// counter.c
int counter_get_value(const Counter *c) {
return c->value;
}
تقوم دالة الـ setter بتعديل عضو مخفي:
// counter.h
void counter_set_value(Counter *c, int new_value);
// counter.c
void counter_set_value(Counter *c, int new_value) {
c->value = new_value;
}
استخدم اصطلاح تسمية مع اسم النوع كبادئة (counter_) لتجنب التعارضات وتوضيح واجهة برمجة التطبيقات (API).
يتفاعل الكود الخارجي مع البيانات المخفية من خلال هذه الدوال:
// main.c
Counter *c = create_counter(10);
printf("%d\n", counter_get_value(c)); // 10
counter_set_value(c, 25);
printf("%d\n", counter_get_value(c)); // 25
جرّب بنفسك
#include <stdio.h>
#include "score.h"
int main() {
int initial_points, new_points;
scanf("%d", &initial_points);
scanf("%d", &new_points);
// TODO: قم بإنشاء Score باستخدام initial_points
// TODO: اطبع القيمة الأولية باستخدام الـ getter
// التنسيق: "Initial: {points}"
// TODO: قم بتحديث النتيجة باستخدام الـ setter مع new_points
// TODO: اطبع القيمة المحدثة باستخدام الـ getter
// التنسيق: "Updated: {points}"
// TODO: قم بتحرير الـ score واطبع "Freed"
return 0;
}
يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.
جميع دروس Object Oriented Programming
1أساسيات البرمجة التركيبية
ملفات الترويسةحواجز التضمينملفات المصدرالدوال الساكنةمراجعة: آلة حاسبة تركيبية4التغليف (Encapsulation)
مفهوم الـ Opaque Pointersتعريف الـ Opaque Structsالـ Getters والـ Settersالتحقق من البيانات في الـ Settersملخص: الصندوق السري2الكائنات والأساليب
الـ Structs ككائناتمؤشر 'Self'صحة استخدام Constالمؤشر مقابل القيمةالأساليب المساعدةملخص: Point Manager5مشروع: حساب بنكي بسيط
إعداد المشروعتنفيذ الحساب3دورة حياة الكائن
نمط المنشئ (Constructor Pattern)نمط الهادم (Destructor Pattern)تهيئة الـ Stackالنسخ العميق (Deep Copy)مراجعة: String Wrapper6الوراثة عبر التركيب
تضمين الـ Structقاعدة العضو الأولالوصول إلى أعضاء الأبعملية الـ Upcastingمراجعة: هرمية الأشكال9مشروع: رسّام الأشكال
نظرة عامة على المشروعتنفيذ الدائرة