نمط Singleton
جزء من قسم البرمجة كائنية التوجه في رحلة C على Coddy. الدرس 54 من 61.
أحيانًا تحتاج إلى instance واحد فقط من كائن طوال برنامجك بالكامل: مدير إعدادات، أو نظام تسجيل، أو اتصال بقاعدة بيانات. يضمن نمط Singleton وجود instance واحدة only ويوفر نقطة وصول عامة إليها.
في لغة C، نُنفّذ ذلك باستخدام متغيّر static داخل ملف مصدر. تذكّر أن static على مستوى الملف يعني أن المتغيّر خاص بذلك الملف ويستمر طوال عمر البرنامج. وبدمجه مع دالة getter، ينشئ ذلك 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(). ينشئ call first instance ويهيّئها. كل call لاحق يعيد same pointer.
تضمن الكلمة المفتاحية static أن تحتفظ instance بقيمتها بين استدعاءات الدوال وأن تظل مخفية عن الملفات الأخرى.
من ملف الترويسة، لا يرى المستخدمون سوى دالة getter. ولا يمكنهم الوصول إلى المتغير static أو تعديله مباشرةً. وبغضّ النظر عن عدد المرات أو المكان الذي تستدعي منه get_config()، ستحصل دائمًا على الكائن نفسه.
التحدي
سهللنبنِ وحدة Settings تدير إعدادات التطبيق على مستوى التطبيق باستخدام نمط Singleton. بغض النظر عن عدد المرات التي يطلب فيها الكود الإعدادات، يجب أن يتلقى دائمًا نفس instance تمامًا.
ستنظم الكود عبر ثلاثة ملفات:
settings.h: Declare نوعSettingsopaque باستخدام forward declaration (يبقى جسم البنية مخفيًا). Declare دالةget_settingsالتي returns مؤشرًا إلى singleton instance، بالإضافة إلى دوال getter وsetter لقيمتي إعداد:volume(عدد صحيح) وbrightness(عدد صحيح). تُعد Include guards ضرورية هنا.settings.c: هنا يحدث السحر. Define بنيةSettingsالفعلية معvolumeوbrightnessfields. Create متغير مؤشرstaticللاحتفاظ بـ single instance (تمت تهيئته إلىNULL). Implementget_settingsبحيث يقومfirstCall بتخصيص instance وتهيئته بالقيم الافتراضية (volume: 50، brightness: 75)، بينما تقوم calls اللاحقة ببساطة بإرجاع existing instance. Implement دوال getter وsetter للوصول إلى الحقول المخفية وتعديلها.main.c: Demonstrate أن Singleton يعمل بشكل صحيح. اقرأ عددين صحيحين يمثلان قيمتي volume وbrightness الجديدتين. Callget_settingstwice، مع تخزين النتائج في two different pointer variables. استخدمfirstpointer لتحديث volume وbrightness باستخدام قيم input. ثم استخدمsecondpointer لاسترجاع كلتا القيمتين وطباعتهما. إذا كان Singleton يعمل، فستكون التغييرات التي أُجريت من خلال مؤشر واحد مرئية من خلال المؤشر الآخر.
سيتلقى برنامجك مدخلين:
- قيمة volume الجديدة
- قيمة brightness الجديدة
اطبع volume وbrightness في سطرين منفصلين بعد تعديلهِما من خلال مؤشر واحد وقراءتهِما من خلال مؤشر آخر.
مثال على المخرجات عندما تكون المدخلات 80 و100:
80
100مثال على المخرجات عندما تكون المدخلات 25 و60:
25
60مثال على المخرجات عندما تكون المدخلات 0 و50:
0
50الفكرة الأساسية هي أن both pointers يشيران إلى نفس الكائن في الذاكرة: وهذا هو جوهر نمط Singleton. يضمن المتغير static داخل ملفك المصدر وجود only one instance على الإطلاق، ويستمر طوال lifetime البرنامج بالكامل.
جرّب بنفسك
#include <stdio.h>
#include "settings.h"
int main() {
int new_volume, new_brightness;
scanf("%d", &new_volume);
scanf("%d", &new_brightness);
// TODO: استدعِ get_settings مرتين وخزّن في متغيري مؤشر مختلفين
// TODO: استخدم المؤشر الأول لتحديث volume و brightness بقيم الإدخال
// TODO: استخدم المؤشر الثاني لاسترجاع وطباعة volume و brightness
// (هذا يوضح أن كلا المؤشرين يشيران إلى نفس مثيل singleton)
return 0;
}
يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.
جميع دروس البرمجة كائنية التوجه
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مشروع: رسّام الأشكال
نظرة عامة على المشروعتنفيذ الدائرةتنفيذ المستطيلاستخدام تعدد الأشكالحاوية الأشكالتدرّب بنفسك: مترجم C عبر الإنترنت