عند اختبار الكود تظهر النتيجة بالشكل التالي
object(TEST)#6 (1) { ["_var"]=> string(4) "test" } object(TEST)#7 (1) { ["_var"]=> string(4) "test" }لاحظ الرقم الموجود هنا object(TEST)#6 وهنا object(TEST)#7 فهما مختلفين دلالة على اختلاف كل منهما عن الآخر. فضلا تذكر هذا جيداً
وهنا أيضاً ربما تفكر في حل لهذه المشكلة بأن لا تقوم بإنشاء كائن من الكلاس MAIN داخل أي من TEST_1 أو TEST_2 ،ولكن تقوم بإنشاء كائن واحد من الكلاس MAIN في الملف الذي يتم استخدام كلا من TEST_1 أو TEST_2 فيه (أي تجعله global) وبالتالي تم حل المشكلة وأيضاً تم استخدم الكلاس MAIN مرة واحدة.
ولكن المشكلة هنا تكمن في أن الكود داخل كلا من TEST_1 أو TEST_2 معتمد على الكلاس MAIN وبالتالي لايمكن فصلها عن هذه الكلاسات لذلك، هنا نحتاج لأسلوب يسمح لنا باستخدام نفس البيانات مهما زاد عدد الكائنات وبدون تكرار الوظيفة لأن زيادة العدد لن تغير من البيانات وهو النمط المفرد Singleton pattern
مثال على ذلك، وغالباً هو أكثر مثال يوضح سبب استخدام النمط المفرد، وهو عندما تقوم بعمل كلاس مسؤلة عن التواصل مع قاعدة البيانات، فخلال البرنامج يتم التواصل مع قاعدة البيانات في مواقع كثيرة في نفس الاسكريبت سواء مباشرة أو ضمنياً داخل كلاس أخرى في وقت واحد، وبالتالي ليس هناك الحاجة إلى أن تتواصل مع قاعدة البيانات أكثر من مرة، تكفي مرة واحدة فقط حتى لاستهلك ذاكرة الرنامج في شيء أنت تعرف أنه مهما تكرر يعود بنفس النتيجة.
أريد الآن أن نبدأ في نشرح النمط المفرد singleton pattern بالتفصيل، ولكن أحب أن أثبت كلامي السابق أولاً، لذلك قم باختبار الكود التالي
<?php class TEST { private $_test; //The single instance private static $_instance; // Constructor private function __construct() { $this->_test = 'test'; } /* *Get an instance of the TEST *@return Instance */ public static function getInstance() { if(!self::$_instance) { // If no instance then make one self::$_instance = new self(); } return self::$_instance; } /* *The __clone method is opposit to singleton pattern *because it's purpose is to create a clone of our class *we don't want this so we make it final so it that it can't be overriden *and also we make it empty */ final private function __clone() { } /* *The __wakeup method is called when a php object is unserialized *but if an object is unserialized will make it unserialzed to multiple object *we don't want this so we make it final so it that it can't be overriden *and also we make it empty */ final private function __wakeup() { } public function getTest() { return $this->_test; } } $test1 = TEST::getInstance(); $test2 = TEST::getInstance(); echo '<pre>'; var_dump($test1); var_dump($test2); echo '</pre>';عند اختبار الكود تكون النتيجة
object(TEST)#6 (1) { ["_test":"TEST":private]=> string(4) "test" } object(TEST)#6 (1) { ["_test":"TEST":private]=> string(4) "test" }لاحظ تم استرجاع كائنين بنفس الرقم object(TEST)#6 ، وهذا يعني أنه لم يقم بعمل كائن جديد وإنما قام باستخدام نفس الكائن من الذاكرة.
شرح النمط المفرد Singleton patternيتم استخدام النمط المفرد عن الحاجة لمنع عمل أكثر من كائن من نفس الكلاس مادام الكائن يحتوي على بيانات ثابته
تكوين النمط المفرد Singleton pattern structureإذا كان الهدف من النمط المفرد هو منع عمل الكائنات فعلينا منع الشيء المسؤل عن إنشاء الكائنات في الكلاس وهو مُنشيء الكلاس PHP Constructor، وبما أننا نعرف أن الكائنات لايمكنها الوصول للقيم الخاصة Private visibility إذن علينا تغيير حدود الرؤية PHP Visibility للمُنشيء إلى Private
<?php class TEST { // Constructor private function __construct() { }وهذا لن تكون قادر نهائياً على عمل كائن بهذه الشكل $test1 = new TEST();
ولكن نحتاج لكائن واحد فقط، وفي نفس لا نستطيع عمل كائن بسبب private function __construct() ، وإنما نستطيع أن نجعل الكلاس تقوم بعمل كائن بنفسها، أي تقوم الكلاس بتخزين نفسها داخل متغير وذلك عن طريق self هكذا
<?php class TEST { //The single instance private static $_instance; // Constructor private function __construct() { } /* *Get an instance of the TEST *@return Instance */ public static function getInstance() { self::$_instance = new self(); return self::$_instance; } }وفي الكود السابق قمنا بعمل أكثر من شيء يجب الإنتباه له
قمنا بتعريف الخاصية $_instance على أنها private لأننا نعرف أنه لن يتم عمل كائنات مباشرة من الكلاس ونعرف أن private تعني خاص بالكلاس فقط أيضاً قمنا بتعريف الخاصية $_instance على أنها Static لسببين السبب الأول: حتى نستطيع استخدام self عند الوصول إليها، لأن بما أنه لايوجد كائنات فلايمكن استخدام $this السبب الثاني: فكما نعرف أن الخصائص الساكنة Static properties تحتفظ بآخر قيمة لها وبالتالي عن استدعاء الكلاس أول مرة سيكون للكائن قيمة ما، ويتم استخدام هذه القيمة عند استدعاء الكلاس مرة أخرى قمنا بعمل الدالة getInstance() والتي عند استدعائها ستقوم بعمل كائن من الكلاس وتخزينه في $_instance عن طريق new self() .الآن عملية إنشاء الكائن متوقفة على الدالة getInstance() ، ولكن كيف نستطيع أن نستدعيها، فنحن نعرف أنه علينا إنشاء كائن حتى نستخدم الكائن في استدعاء الدالة، وفي نفس الوقت لايمكن إنشاء كائن، إذن علينا استخدام الطريقة الأخرى وهي الوصول للدالة من خارج الكلاس هكذا TEST::getInstance()
ولكن لابد أنك تعلم أنه لايمكنك ذلك إلا إذا كانت الطريقة ساكنة Static Method وبالتالي علينا تعديل الكود ليصبح هكذا
<?php class TEST { //The single instance private static $_instance; // Constructor private function __construct() { } /* *Get an instance of the TEST *@return Instance */ public static function getInstance() { self::$_instance = new self(); return self::$_instance; } }الآن ينقصنا أهم شيء، وهو أن نمنع الكلاس من إنشاء نفسها أكثر من مرة واحدة، وبالتالي نحتاج نفحص، في كل مرة يتم فيها استدعاء الكلاس، فيما إذا كان هناك كائن تم إنشاءه سابقاً أن لا، فإن كان كذلك فيسترجعه أما إن لم يكن، فيقوم بعمل الكائن وبالتالي يكون شكل الكود
<?php class TEST { //The single instance private static $_instance; // Constructor private function __construct() { } /* *Get an instance of the TEST *@return Instance */ public static function getInstance() { if(!self::$_instance) { // If no instance then make one self::$_instance = new self(); } return self::$_instance; } }
إلى هنا إذا قمت بعمل أكثر من كائن من الكلاس ستكون النتيجة واحدة بنفس الكائن، لاحظ النتيجة إذا قمنا بعمل كائنين $test1 = TEST::getInstance(); و $test2 = TEST::getInstance();
object(TEST)#1 (0) { } object(TEST)#1 (0) { }لاحظ نفس رقم الكائن object(TEST)#1
ولكن يمكن تخطي سلوك الكلاس إذا تم عمل استنساخ Object cloning لذا يجب علينا منع ذلك باستخدام الطريقة السحرية __clone() ، وبالرغم من __clone() هي الدالة التي تساعد في الاستنساخ ولكن يمكننا أن نعرفها بالشكل الذي يمنع ذلك إما بأن تكون فارغة أو تسترجع خطأ ما عند محاولة استساخ الكائن، وأيضاً نجعلها نهائية Final method حتى لايمكن تخطي سلوكها، وبالتالي يمكننا إضافتها للكود هكذا
/* *The __clone method is opposit to singleton pattern *because it's purpose is to create a clone of our class *we don't want this so we make it final so it that it can't be overriden *and also we make it empty */ final private function __clone() { }أيضاً يمكن تخطي سلوك الكلاس إذا تم عمل Object Unserialize لذا يجب علينا منع ذلك باستخدام الطريقة السحرية __wakeup() ، وبالرغم من __wakeup() هي الدالة التي تساعد في الاستنساخ ولكن يمكننا أن نعرفها بالشكل الذي يمنع ذلك إما بأن تكون فارغة أو تسترجع خطأ ما عند محاولة استساخ الكائن، وأيضاً نجعلها نهائية Final method حتى لايمكن تخطي سلوكها، وبالتالي يمكننا إضافتها للكود هكذا
/* *The __wakeup method is called when a php object is unserialized *but if an object is unserialized will make it unserialzed to multiple object *we don't want this so we make it final so it that it can't be overriden *and also we make it empty */ final private function __wakeup() { }وبالتلي يكون الشكل النهائي للنمط المفرد كالتالي
<?php class TEST { //The single instance private static $_instance; // Constructor private function __construct() { } /* *Get an instance of the TEST *@return Instance */ public static function getInstance() { if(!self::$_instance) { // If no instance then make one self::$_instance = new self(); } return self::$_instance; } /* *The __clone method is opposit to singleton pattern *because it's purpose is to create a clone of our class *we don't want this so we make it final so it that it can't be overriden *and also we make it empty */ final private function __clone() { } /* *The __wakeup method is called when a php object is unserialized *but if an object is unserialized will make it unserialzed to multiple object *we don't want this so we make it final so it that it can't be overriden *and also we make it empty */ final private function __wakeup() { } }أتمنى الآن أن تكون اتضحت الصورة وما هي فوائد النمط المفرد singleton pattern، وكما قلنا أن أحد الإستخدامات الشائعة له هو كلاس الإصال بقاعدة البيانات وإليك الكود الخاص بذلك
class DBCONN { private $_connection; private static $_instance; //The single instance private $_host = HOST; private $_username = DB_USER; private $_password = DB_PASS; private $_database = DB_NAME; // Constructor private function __construct() { $this->_connection = new mysqli($this->_host, $this->_username, $this->_password, $this->_database); } /* Get an instance of the Database @return Instance */ public static function getInstance() { if(!self::$_instance) { // If no instance then make one self::$_instance = new self(); } return self::$_instance; } /* *The __clone method is opposit to singleton pattern *because it's purpose is to create a clone of our class *we don't want this so we make it final so it that it can't be overriden *and also we make it empty */ final private function __clone() { } /* *The __wakeup method is called when a php object is unserialized *but if an object is unserialized will make it unserialzed to multiple object *we don't want this so we make it final so it that it can't be overriden *and also we make it empty */ final private function __wakeup() { } // Get mysqli connection public function getConnection() { return $this->_connection; } }وكل ما عليك هو استبدال معلومات قاعدة البيانات الخاصة بك ثم ابدأ في استخدام الكلاس

