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

التحقق من البيانات في الـ Setters

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

في الدرس السابق، قمنا بإنشاء دوال تعيين (setters) أساسية تقوم ببساطة بتعيين القيم. ولكن هذا يغفل فائدة رئيسية للتغليف (encapsulation): القدرة على حماية بياناتك عن طريق إضافة منطق التحقق من الصحة (validation logic).

ضع في اعتبارك Counter لا ينبغي أبداً أن يصبح سالباً. بدون التغليف (encapsulation)، يمكن لأي شخص كتابة c->value = -50 وإفساد الحالة. باستخدام setter، يمكنك التحكم في القيم المسموح بها:

// counter.c
void counter_set_value(Counter *c, int new_value) {
    if (new_value < 0) {
        return;  // رفض المدخلات غير الصالحة بصمت
    }
    c->value = new_value;
}

الآن، يتم تجاهل القيم السالبة ببساطة.

قد تختار أيضًا إرجاع رمز حالة للإشارة إلى النجاح أو الفشل:

int counter_set_value(Counter *c, int new_value) {
    if (new_value < 0) {
        return 0;  // فشل
    }
    c->value = new_value;
    return 1;  // نجاح
}

يتيح هذا النمط للمستدعي معرفة ما إذا كانت العملية قد نجحت أم لا. تكمن الفكرة الجوهرية في أن دوال التعيين (setters) لا تقتصر فقط على عملية الإسناد، بل هي بمثابة حراس يفرضون القواعد المتعلقة بما يمكن أن تكون عليه حالة الكائن الخاص بك. هذه هي القوة الحقيقية للتغليف (encapsulation): حيث يحافظ الكائن الخاص بك على سلامته الخاصة، بغض النظر عما يحاول الكود الخارجي القيام به.

challenge icon

التحدي

سهل

لنقم ببناء وحدة Temperature تحمي بياناتها باستخدام التحقق من الصحة في الـ setter. ستقوم بإنشاء متتبع لدرجة الحرارة يقبل فقط القيم ضمن نطاق صالح - مما يوضح كيف تعمل الـ setters كحراس لبوابة حالة الكائن الخاص بك.

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

  • temperature.h: واجهتك العامة باستخدام نمط المؤشر المعتم (opaque pointer pattern). قم بالتصريح عن نوع Temperature باستخدام تصريح مسبق (forward declaration) فقط. قم بالتصريح عن هذه الدوال:
    • create_temperature — تأخذ عدداً صحيحاً للدرجات الأولية وتعيد مؤشر Temperature.
    • free_temperature — تحرر الذاكرة المخصصة.
    • temp_get_degrees — دالة getter تعيد قيمة درجة الحرارة الحالية.
    • temp_set_degrees — دالة setter تعيد int (1 للنجاح، 0 للفشل).
    استخدم حراس التضمين (include guards) مع الرمز TEMPERATURE_H.
  • temperature.c: قم بتعريف struct Temperature الفعلي مع عضو int degrees مخفي. قم بتنفيذ جميع الدوال. المفتاح هو الـ setter الخاص بك - يجب أن يقبل فقط القيم بين -50 و 150 (شاملة). إذا كانت القيمة خارج هذا النطاق، فقم بإرجاع 0 واترك درجة الحرارة دون تغيير. إذا كانت صالحة، فقم بتحديث القيمة وأرجع 1.
  • main.c: قم بتوضيح التحقق من الصحة من خلال محاولة تعيين درجات حرارة صالحة وغير صالحة، مما يوضح أن القيم غير الصالحة يتم رفضها بينما يتم قبول القيم الصالحة.

ستتلقى ثلاثة مدخلات: درجة الحرارة الأولية، و درجة حرارة صالحة لتعيينها (ضمن النطاق)، و درجة حرارة غير صالحة لمحاولة تعيينها (خارج النطاق).

في ملفك الرئيسي، قم بإنشاء Temperature بالقيمة الأولية واطبعها. ثم حاول تعيين درجة الحرارة غير الصالحة - وبما أنه يجب رفضها، اطبع النتيجة وأظهر أن القيمة تظل دون تغيير. أخيراً، قم بتعيين درجة الحرارة الصالحة، واطبع نتيجة النجاح، وأظهر القيمة المحدثة. قم بتحرير الذاكرة وتأكيد عملية التنظيف.

اطبع المخرجات بهذا التنسيق:

Initial: {degrees}
Set {invalid}: {0 or 1}
After invalid: {degrees}
Set {valid}: {0 or 1}
After valid: {degrees}
Freed

على سبيل المثال، مع المدخلات 25، 100، و 200، ستكون المخرجات:

Initial: 25
Set 200: 0
After invalid: 25
Set 100: 1
After valid: 100
Freed

يوضح هذا القوة الحقيقية للتغليف (encapsulation) - حيث يفرض الـ setter الخاص بك القواعد المتعلقة بما يشكل درجة حرارة صالحة، مما يحمي سلامة الكائن بغض النظر عن القيم التي يحاول الكود الخارجي تعيينها.

ورقة مرجعية

يمكن أن تتضمن دوال التعيين (Setters) منطق تحقق لحماية سلامة البيانات. فبدلاً من مجرد تعيين القيم، تعمل هذه الدوال كـ حراس يفرضون القواعد المتعلقة بحالات الكائنات الصالحة.

مثال بسيط على التحقق - رفض المدخلات غير الصالحة بصمت:

void counter_set_value(Counter *c, int new_value) {
    if (new_value < 0) {
        return;  // رفض المدخلات غير الصالحة
    }
    c->value = new_value;
}

إرجاع رمز حالة للإشارة إلى النجاح أو الفشل:

int counter_set_value(Counter *c, int new_value) {
    if (new_value < 0) {
        return 0;  // فشل
    }
    c->value = new_value;
    return 1;  // نجاح
}

يسمح هذا النمط للمستدعين بالتحقق مما إذا كانت العملية قد نجحت، مما يمكّن الكائنات من الحفاظ على سلامتها بغض النظر عن محاولات الكود الخارجي.

جرّب بنفسك

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

int main() {
    // قراءة المدخلات
    int initial, valid_temp, invalid_temp;
    scanf("%d", &initial);
    scanf("%d", &valid_temp);
    scanf("%d", &invalid_temp);
    
    // TODO: إنشاء Temperature بالقيمة الأولية
    
    // TODO: طباعة درجة الحرارة الأولية
    // التنسيق: "Initial: {degrees}"
    
    // TODO: محاولة تعيين درجة الحرارة غير الصالحة
    // طباعة النتيجة وإظهار أن القيمة تظل دون تغيير
    // التنسيق: "Set {invalid}: {0 or 1}"
    // التنسيق: "After invalid: {degrees}"
    
    // TODO: تعيين درجة الحرارة الصالحة
    // طباعة نتيجة النجاح وإظهار القيمة المحدثة
    // التنسيق: "Set {valid}: {0 or 1}"
    // التنسيق: "After valid: {degrees}"
    
    // TODO: تحرير الذاكرة وطباعة "Freed"
    
    return 0;
}
quiz iconاختبر نفسك

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

جميع دروس Object Oriented Programming