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

نمط Singleton

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

أحياناً تحتاج إلى نسخة واحدة بالضبط من كائن ما طوال برنامجك بالكامل — مدير إعدادات، أو نظام تسجيل، أو اتصال بقاعدة بيانات. يضمن نمط Singleton وجود نسخة واحدة فقط ويوفر نقطة وصول عامة إليها.

في لغة C، نقوم بتنفيذ ذلك باستخدام متغير static داخل ملف المصدر. تذكر أن static على نطاق الملف يعني أن المتغير خاص بهذا الملف ويستمر طوال فترة حياة البرنامج. وبدمجه مع دالة جلب (getter function)، فإن هذا ينشئ الـ Singleton الخاص بنا:

// config.c
#include "config.h"
#include <stdlib.h>

struct Config {
    int max_connections;
    int timeout;
};

static Config* instance = NULL;

Config* get_config(void) {
    if (instance == NULL) {
        instance = malloc(sizeof(Config));
        instance->max_connections = 100;
        instance->timeout = 30;
    }
    return instance;
}

يحدث السحر في get_config(). يقوم الاستدعاء الأول بإنشاء وتهيئة المثيل، وكل استدعاء لاحق يعيد نفس المؤشر.

تضمن الكلمة المفتاحية static أن instance يحتفظ بقيمته بين استدعاءات الدوال ويظل مخفياً عن الملفات الأخرى.

من الترويسة، يرى المستخدمون فقط دالة الجلب (getter function) — ولا يمكنهم الوصول إلى المتغير الساكن (static variable) أو تعديله مباشرة. وبغض النظر عن عدد المرات أو المكان الذي تستدعي منه get_config()، ستحصل دائماً على الكائن نفسه.

challenge icon

التحدي

سهل

لنقم ببناء وحدة Settings تدير تكوينات التطبيق بالكامل باستخدام نمط Singleton. بغض النظر عن عدد المرات التي يطلب فيها الكود الخاص بك الإعدادات، يجب أن يتلقى دائماً نفس النسخة (instance) تماماً.

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

  • settings.h: قم بالتصريح عن نوع Settings مبهم باستخدام تصريح مسبق (يبقى جسم الـ struct مخفياً). صرّح عن دالة get_settings التي تعيد مؤشراً إلى نسخة الـ singleton، بالإضافة إلى دوال getter و setter لقيمتي تكوين: volume (عدد صحيح) و brightness (عدد صحيح). حواجز التضمين (Include guards) ضرورية هنا.
  • settings.c: هنا يحدث السحر. قم بتعريف الـ Settings struct الفعلي مع حقول volume و brightness. أنشئ متغير مؤشر static للاحتفاظ بالنسخة الوحيدة (يتم تهيئته بـ NULL). قم بتنفيذ get_settings بحيث تقوم الاستدعاءة الأولى بتخصيص الذاكرة وتهيئة النسخة بالقيم الافتراضية (volume: 50، brightness: 75)، بينما تعيد الاستدعاءات اللاحقة ببساطة النسخة الموجودة. قم بتنفيذ الـ getters والـ setters للوصول إلى الحقول المخفية وتعديلها.
  • main.c: أثبت أن الـ Singleton يعمل بشكل صحيح. اقرأ عددين صحيحين يمثلان قيم مستوى الصوت والسطوع الجديدة. استدعِ get_settings مرتين، وقم بتخزين النتائج في متغيري مؤشر مختلفين. استخدم المؤشر الأول لتحديث مستوى الصوت والسطوع بالقيم المدخلة. ثم استخدم المؤشر الثاني لاسترداد وطباعة كلتا القيمتين. إذا كان الـ Singleton يعمل، فإن التغييرات التي تم إجراؤها من خلال مؤشر واحد ستكون مرئية من خلال الآخر.

سيتلقى برنامجك مدخلين:

  1. قيمة مستوى الصوت الجديدة
  2. قيمة السطوع الجديدة

اطبع مستوى الصوت والسطوع في سطرين منفصلين بعد تعديلهما من خلال مؤشر واحد وقراءتهما من خلال مؤشر آخر.

مثال على المخرجات عندما تكون المدخلات 80 و 100:

80
100

مثال على المخرجات عندما تكون المدخلات 25 و 60:

25
60

مثال على المخرجات عندما تكون المدخلات 0 و 50:

0
50

الفكرة الأساسية هي أن كلا المؤشرين يشيران إلى نفس الكائن في الذاكرة — وهذا هو جوهر نمط Singleton. يضمن المتغير الساكن (static) داخل ملف المصدر الخاص بك وجود نسخة واحدة فقط على الإطلاق، وتستمر طوال فترة حياة البرنامج.

ورقة مرجعية

يضمن نمط Singleton وجود مثيل واحد فقط للكائن طوال البرنامج بأكمله ويوفر نقطة وصول عالمية إليه.

في لغة C، قم بتنفيذ Singleton باستخدام متغير static داخل ملف مصدر مدمج مع دالة جلب (getter function):

// config.c
#include "config.h"
#include <stdlib.h>

struct Config {
    int max_connections;
    int timeout;
};

static Config* instance = NULL;

Config* get_config(void) {
    if (instance == NULL) {
        instance = malloc(sizeof(Config));
        instance->max_connections = 100;
        instance->timeout = 30;
    }
    return instance;
}

النقاط الرئيسية:

  • تجعل الكلمة المفتاحية static على نطاق الملف المتغير خاصاً بذلك الملف ومستمراً طوال عمر البرنامج
  • تتحقق دالة الجلب (getter function) مما إذا كان المثيل NULL عند الاستدعاء الأول، وتقوم بإنشائه وتهيئته، ثم تعيده
  • الاستدعاءات اللاحقة تعيد نفس المؤشر
  • يرى المستخدمون دالة الجلب فقط من خلال ملف الترويسة (header) — ولا يمكنهم الوصول إلى المتغير الساكن أو تعديله مباشرة
  • بغض النظر عن عدد المرات أو المكان الذي تستدعي منه دالة الجلب، ستحصل دائماً على نفس الكائن

جرّب بنفسك

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

int main() {
    int new_volume, new_brightness;
    scanf("%d", &new_volume);
    scanf("%d", &new_brightness);
    
    // TODO: استدعِ get_settings مرتين وقم بتخزينهما في متغيري pointer مختلفين
    
    
    // TODO: استخدم الـ pointer الأول لتحديث volume و brightness بالقيم المدخلة
    
    
    // TODO: استخدم الـ pointer الثاني لاسترجاع وطباعة volume و brightness
    // (هذا يوضح أن كلا الـ pointers يشيران إلى نفس الـ singleton instance)
    
    
    return 0;
}
quiz iconاختبر نفسك

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

جميع دروس Object Oriented Programming