Singleton design pattern


السلام عليكم و رحمه الله تعالى و بركاته

هذا الموضوع يعتمد على ماتم شرحه فى سلسله جافاسكربت الموجهه بالكائنات .

Singleton هو نمط تصميم يستخدم لتقييد انشاء الصنف إلى كائن واحد فقط .

The Singleton pattern is a design pattern that is used to restrict instantiation of a class to one object .
WikiPedia

وهو من اكثر انماط التصميم استخداما و ربما استخدمته و انت لا تدرى ، لأن ابسط صوره لل Singleton هو الكائن الذى تم اتنشاءه من Object Literal ، كما يوضح المثال البسيط التالى :

var Singleton = {
    propertyOne :"value",
    propertyTwo:"someValue",
    methodOne:function(){/*code*/},
    methodTwo:function(){/*code*/},
}

لا يوجد شىء غريب هنا ، و بالطبع كما تعلمنا فى سلسله OOP JavaScript يمكنك استخدام هذا ال Singleton كما يلى :

Singleton.propertyOne; // value
Singleton.methodTwo(); // execute it

لكن من عيوب انشاء Singleton عن طريق object literal مباشرا ، ان الكائن الناتج يكون fully exposed object اي ان كل اعضائه من خواص ووظائف تصبح عامه الوصول publicly accessible و لا يمكنك عمل private variables مباشرا عن طريق هذا ال object literal ، حل هذه المشكله هو عمل closure عن طريق self invoking anonymous function لحمايه المتغيرات الخاصه و إرجاع object literal كما يوضح الكود البسيط التالى :

var Singleton = (function(){
    var secret = "Iam working on it now";
    function secretFunction(){
        /*code*/
    }

    return {
        propertyOne :"value",
        propertyTwo:"someValue",
        methodOne:function(){/*code*/},
        methodTwo:function(){/*code*/},
     }
})(); // function executes without calling

قمنا بتقدم المتغير الخاص secert و الوظيفه الخاصه secretFunction عن طريق ال closure الناتج من الوظيفه المحيطه و التى تتنفذ تلقائيا و تقوم بلصق ال object literal إلى المتغير Singleton ، بالطبع وظائف هذا ال Singleton تستطيع استخدام هذا المتغيرات الخاصه ، و يمكننا استخدامه كالتالى :

Singleton.propertyOne; // value
Singleton.methodTwo(); // execute it
Singleton.secret; // undefined

الطريقه السابقه فى انشاء ال Singleton تسمى ايضا بال Module pattern .

يستخدم ال Singleton فى عمل namespace من اجل تجميع وظائف الاطار حسب التشابه بينهم تحت إسم متغير واحد حتى لا يتلوث ال global scope كما يوضح المثال البسيط التالى :

// if Keepondev is undefined, create new Object
var Keepondev = keepondev || {};

Keepondev.DOM = { // Document Object Model Singleton
    length:0,
    getByID:function(id){/*code*/},
    getByClass:function(class){/*code*/},
    remove:function(elem){/*code*/},
    append:function(elem){/*code*/},
    /* rest of Keepondev.DOM functions*/
}

Keepondev.IO = (function(){

    var privateAttribute = "something";
    function privateFunction(){
        /*implementation code*/
    }

    return { // Input/Output Singleton
        open:function(dir){/*code*/},
        close:function(file){/*code*/},
        read:function(file,data){/*code*/},
        write:function(file,data){/*code*/},
        append:function(file,date){/*code*/},
        delete:function(file){/*code*/},
        /* rest of Keepondev.DOM functions*/
    }

})();

قد تناولت موضوع ال namespace هذا من قبل فى سلسه OOP JavaScript ، عليك ملاحظه ان Keepondev.DOM كل وظائفه عامه ، اما Keepondev.IO به بعض الاعضاء الخاصه لأنى قمت بعمل closure و ارجعت object literal يحتوى على خواص و وظائف عامه ،  يمكننا استخدام الوظائف داخل ال namespace كما يلى :

// using Keepondev.DOM
var myBtn = Keepondev.DOM.getById("myBtn");
Keepondev.DOM.remove(myBtn);
// or reference Keepondev.DOM in $
var $ = keepondev.DOM ;
var myBtn = $.getById("myBtn");
$.remove(myBtn);

// using Keepondev.IO
var readMe = Keepondev.IO.open("C:/readMe.txt");
Keepondev.IO.delete(readMe);

الطريقتين السابقتين فى انشاء ال Singleton يشتركان فى ان ال Singleton يتم انشاءه فور تحميل التطبيق لا فور استخدامه ، لا توجد مشكله فى ذلك اذا كنت تستخدم ال Singleton فى عمل namespace او اذا كان ال singleton لا يستهلك الكثير من الموارد من الذاكره ، اما بالنسبه لل Singleton الذى يقوم بالكثير من العمليات و جلب البيانات و الذى يحتوى على اكثير من الوظائف و الخصائص التى يتم بنائها وقت التشغيل ، قد يكون من المناسب و الافضل لكفاءه التطبيق ان بتم بناءه فقط عند الحاجه إليه ، يعرف تكنيك البناء عند الحاجه إلى الاستخدام ب Lazy loading التحميل الكسول ، لن يتغير تصميم ال Singleton كثيرا ، سننقل الكود الخاص بال Singleton داخل وظيفه بإسمه constructor ، هذه الوظيفه ستكون غير متاحه من خارج ال singleton إلا بعد استدعاء وظيفه getInstance كما يوضح الكود التالى الذى يعدل على اول Singleton تم انشاءه من خلال Closure :

var Singleton = (function(){
    var uniqueInstance;    

    function constructor(){
        var secret = "Iam working on it now";
        function secretFunction(){
        /*implementation code*/
        }

        return {
            propertyOne :"value",
            propertyTwo:"someValue",
            methodOne:function(){/*code*/},
            methodTwo:function(){/*code*/},
         }
    }

    return {
        getInstance:function(){
            if(!uniqueInstance){
                uniqueInstance = constructor();
            }
            return uniqueInstance;
        }
    }

})(); // function executes without calling

و بذلك ستتحول Singleton إلى التالى :

var Singleton = {
    getInstance:function(){
        if(!uniqueInstance){
            uniqueInstance = constructor();
        }
        return uniqueInstance;
    }
}

و بذلك Singleton سيتحول كما ترى إلى كائن يحتوى على وظيفه واحده getInstance لإنشاء ال Singleton الكبير ، تقوم هذه الوظيفه بفحص ما اذا تم انشاء Singleton من داخل constructor ام لا ، اذا لم يتم انشاءه من قبل يتم ارجاع نسخه جديد ، او يتم ارجاع نسخه تم انشاءها من قبل ، و بذلك ستزداد كفاءه التطبيق لأننا نؤجل انشاء ال Singleton إلى وقت استخدامه و عليه سيتحول استخدام ال singleton من :

Singleton.methodTwo();

إلى :

Singleton.getInstance().methodTwo();

و بدلا من تكرار استخدام الوظيفه getInstance يمكننا خفظ مرجع لل Singleton داخل متعير كما يوضح الكود التالى :

var mySingleton = Singleton.getInstance();
mySingleton.methodTwo();

متى تستخدم Singleton ؟
يستخدم Singleton تقريبا فى اى اطار عمل او كود صغير رأيته ، من اجل وضع الكود تحت namespace واحد و من اجل تقسيم الكود حسب التشابه فى الوظائف حتى يصبح اكثر معياريه Modular ، كما ان ال namespace يقلل من احتمال التصادم بين الاطارات ، فى المثال إستخدمت namespace بإسم Keepondev و وضعت الكود كله تحته مثل Keepondev.DOM.getByID ، هناك فرصه نادره ان شخص ما يستخدم Keepondev اسما لأطار عمل يقوم بكتابته ، فى الواقع مثلا تستخدم مكتبه YUI ال namespace بإسم Yahoo كالتالى Yahoo.util.get … الخ ، كما ان الكود سيكون مفهوما اكثر بالنسبه للمبتدئين فى استخدامه لأن كل الوظائف المتشابهه ستكون تحت namespace اسمه متوقع .

هذا الموضوع هو تلخيص الفصل الخامس من كتاب APRESS: Pro JavaScript Design Patterns .

الأوسمة:

4 تعليقات to “Singleton design pattern”

  1. almhajer Says:

    خلصت من هذه المقالة انه لاتموت الا يجيك الموت
    يعطيك العافية سلمت يداك

  2. mostafa farghaly Says:

    @المهاجر : مش فاهم اول سطر انت كتبته ، لكن عموما شكرا لمتابعتك

  3. almhajer Says:

    قصدت من المثل انك لاتستخدم اي شيء الا اذا كنت بحاجته وهو هنا الذاكرة وكيفية بناء الاشياء عن الحاجة لها دون بناءها بشكل عشاوئي لان احتمالية عدم استخدامها مطلقا اي مثلا انت اضفت مميزات جافا سكربت انميشن ممكن ينتقل الزائر الى صفحة اخرى دون ان يضغط او يشكل هذه المميزة فبذلك تكون قد وفرت استخدام الذاكرة شكرا جزيلا وتنمياتي لك بالتفوفيق

  4. ismail Says:

    code javascript

أضف تعليقاً

إملأ الحقول أدناه بالمعلومات المناسبة أو إضغط على إحدى الأيقونات لتسجيل الدخول:

WordPress.com Logo

أنت تعلق بإستخدام حساب WordPress.com. تسجيل خروج   / تغيير )

صورة تويتر

أنت تعلق بإستخدام حساب Twitter. تسجيل خروج   / تغيير )

Facebook photo

أنت تعلق بإستخدام حساب Facebook. تسجيل خروج   / تغيير )

Google+ photo

أنت تعلق بإستخدام حساب Google+. تسجيل خروج   / تغيير )

Connecting to %s


%d مدونون معجبون بهذه: