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

نمط Singleton

جزء من قسم Object Oriented Programming في رحلة PHP على Coddy — الدرس 71 من 91.

إنّ نمط السينجلتون (Singleton Pattern) هو نمط تصميم إنشائي يضمن وجود مثيل واحد فقط للفئة (class) طوال دورة حياة تطبيقك. كما يوفر أيضاً نقطة وصول عامة لهذا المثيل.

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

إليك كيفية تنفيذ نمط Singleton في PHP:

<?php
class Database {
    private static ?Database $instance = null;
    
    private function __construct() {
        // يمنع المنشئ الخاص إنشاء الكائنات بشكل مباشر
    }
    
    public static function getInstance(): Database {
        if (self::$instance === null) {
            self::$instance = new self();
        }
        return self::$instance;
    }
    
    public function query(string $sql): string {
        return "Executing: $sql";
    }
}

$db1 = Database::getInstance();
$db2 = Database::getInstance();

echo $db1 === $db2 ? "Same instance" : "Different instances";

المخرجات:

Same instance

يعتمد النمط على ثلاثة عناصر رئيسية: منشئ خاص (private constructor) يمنع استخدام new مباشرة، وخاصية ثابتة (static property) تحتفظ بالنسخة الوحيدة، ودالة ثابتة (static method) تقوم بإنشاء النسخة عند الاستدعاء الأول وتعيدها في الاستدعاءات اللاحقة.

لجعل نمط Singleton مضاداً للأخطاء حقاً، يجب عليك أيضاً منع الاستنساخ (cloning) وإلغاء التسلسل (unserialization):

<?php
private function __clone() {}
public function __wakeup() {
    throw new Exception("Cannot unserialize singleton");
}
challenge icon

التحدي

سهل

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

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

  • Config.php — أنشئ فئة Config تطبق نمط Singleton. يجب أن يقوم مدير الإعدادات الخاص بك بـ:
    • تخزين الإعدادات في خاصية مصفوفة خاصة (private array property)
    • امتلاك منشئ خاص (private constructor) يقوم بتهيئة مصفوفة الإعدادات كفارغة
    • توفير تابع ثابت getInstance() يعيد النسخة الوحيدة
    • منع الاستنساخ عن طريق جعل __clone() خاصاً
    • تضمين تابع set(string $key, string $value): void لتخزين إعداد
    • تضمين تابع get(string $key): ?string يعيد القيمة لمفتاح معين، أو null إذا كان المفتاح غير موجود
    • تضمين تابع getAll(): array يعيد جميع الإعدادات
  • main.php — قم بتضمين ملف Config. ستتلقى مدخلين: مفتاح وقيمة.

    احصل على نسخة Config وقم بتعيين إعداد افتراضي بالمفتاح "app_name" والقيمة "MyApp".

    احصل على مرجع ثانٍ لنسخة Config (باستخدام getInstance() مرة أخرى) واستخدمه لتعيين إعداد آخر باستخدام المفتاح والقيمة من المدخلات.

    أخيراً، تحقق من أن Singleton يعمل بشكل صحيح:

    • اطبع "Same instance: true" أو "Same instance: false" بناءً على ما إذا كان كلا المرجعين يشيران إلى نفس الكائن (استخدم === للمقارنة)
    • اطبع "Settings count: [count]" موضحاً العدد الإجمالي للإعدادات المخزنة (استخدم count() على نتيجة getAll())
    • اطبع "App name: [value]" موضحاً إعداد app_name الذي تم استرداده من خلال المرجع الثاني

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

ورقة مرجعية

يضمن نمط المفردة (Singleton Pattern) أن الفئة (class) لديها مثيل واحد فقط طوال دورة حياة تطبيقك ويوفر نقطة وصول عالمية لهذا المثيل.

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

تنفيذ أساسي لنمط Singleton:

<?php
class Database {
    private static ?Database $instance = null;
    
    private function __construct() {
        // المنشئ الخاص يمنع الإنشاء المباشر للمثيل
    }
    
    public static function getInstance(): Database {
        if (self::$instance === null) {
            self::$instance = new self();
        }
        return self::$instance;
    }
}

$db1 = Database::getInstance();
$db2 = Database::getInstance();

echo $db1 === $db2 ? "Same instance" : "Different instances";
// المخرجات: Same instance

العناصر الأساسية:

  • المنشئ الخاص (Private constructor) — يمنع استخدام new مباشرة
  • الخاصية الثابتة (Static property) — تحتفظ بالمثيل الوحيد
  • الدالة الثابتة (Static method) — تنشئ المثيل عند الاستدعاء الأول وتعيده في الاستدعاءات اللاحقة

منع الاستنساخ وإلغاء التسلسل (unserialization):

<?php
private function __clone() {}

public function __wakeup() {
    throw new Exception("Cannot unserialize singleton");
}

جرّب بنفسك

<?php

require_once 'Config.php';

// قراءة المدخلات
$key = trim(fgets(STDIN));
$value = trim(fgets(STDIN));

// TODO: "MyApp" إلى "app_name" وضبط Config الحصول على مثيل

// TODO: مجدداً getInstance() باستخدام Config الحصول على مرجع ثانٍ لـ

// TODO: استخدم المرجع الثاني لضبط الإعداد باستخدام المفتاح والقيمة من المدخلات

// TODO: Singleton التحقق من أن نمط
// === بناءً على مقارنة "Same instance: false" أو "Same instance: true" طباعة -
// يوضح إجمالي الإعدادات "Settings count: [count]" طباعة -
// تم استرداده عبر المرجع الثاني app_name يوضح "App name: [value]" طباعة -

?>
quiz iconاختبر نفسك

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

جميع دروس Object Oriented Programming