proxy design pattern


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

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

مقدمه

نمط التصميم Proxy عباره عن كائن يتحكم فى الوصول -control access- لكائن اخر ، حيث يقوم الكائن proxy بإدراج نفس ال Interface الذى يمتلكه الكائن الاخر الذى يسمى real subject و بذلك يمكن إستخدام الاثنين بالتبادل ، يمكننا إنشاء كائن proxy بدلا من الكائن real subject الذى يأخذ وقت طويل عند انشاءه و يستخدم الكثير من الموارد و بذلك سيتم انشاءه عند الحاجه اليه فقط ، او يمكننا ايضا انشاء كائن proxy ليمثل كائن اخر بعيد remote له نفس ال interface و نتعامل معه كما لو كان فى البيئه الحاليه و انه ليس ببعيد .

تركيب ال Proxy

فى ابسط صوره الكائن Proxy يقوم بالتحكم فى الوصول control access إلى الكائن real subject ، الكائن proxy يقوم بإدراج نفس ال interface الذى يمتكله الكائن real subject ، الكائن real subject هو الذى يقوم بكل المهام ، اما الكائن proxy يقوم بتوجيه الاوامر فقط إلى الكائن real subject .
وجب التنويه ان الكائن Proxy لا يعدل او يضيف اى ميزات جديده على الكائن الاخر كما يفعل نمط التصميم decorator ، ولا يقوم بتبسيط ال interface كما يفعل نمط التصميم Facade ، كل ما يفعله ال proxy هو احاطه الكائن real subject بنفس ال interface و يقوم بتوجيه الاوامر له .

أبسط صور ال Proxy

كما ذكرت من قبل ابسط صوره لل proxy عباره كائن يحيط بالكائن الاصلى بنفس ال interface ، هذا النوع ليس مفيدا لأنه لا يزيد أى ميزه ولا يؤثر على الكفائه ولا على تصميم الكود -إلا اضافه طبقه من التعقيد-، كما يوضح الكود البسيط التالى :

/* MyClass constructor */
function MyClass(){};
MyClass.prototype = {
    method1:function(){},
    method2:function(){},
    method3:function(){},
    method4:function(){},
};

/* MyClass proxy (not practical) */
function MyClassProxy(){
    this.realSubject = new MyClass();
}
MyClassProxy.prototype = {
    method1:function(){
        this.realSubject.method1();
    },
    method2:function(){
        this.realSubject.method2();
    },
    method3:function(){
        this.realSubject.method3();
    },
    method4:function(){
        this.realSubject.method4();
    },
};

هذا النوع من ال proxy غير مفيد لأنه يقوم بإحاطه الكائن الاصلى بنفس ال intercafe و لايقوم بعمل اى شىء غير توجيه الاوامر للكائن الاصلى ، و عليه هذا النوع يمكن انشاءه ديناميكيا كما انشأنا Method profiler ديناميكا فى نمط التصميم Decorator .

أنواع ال Proxy

اول نوع هو ال Virtual Proxy ، الذى يعتبر من اهم انواع ال proxy فى الجافاسكربت ، حيق يقوم بتأخير انشاء الصنف ، يقوم بإنشاءه فقط عند الحاجه اليه – مثل استدعاء احدى وظائفه – ، قد يكون هذا الصنف مكلف جدا فى انشاءه او يكون هذا الصنف ليس له فائده إلا عند الحاجه اليه .
ثانى نوع هو ال Remote Proxy ، الذى يستخدم للوصول لكائن اخر فى بيئه مختلفه ، حيث يتم تمثيل الكائن الاخر البعيد بكائن proxy موجود فى بيئه التشغيل يمكن التفاعل معه نيابا عن الكائن البعيد ، فى عالم ال JAVA قد يعنى هذا كائن اخر فى virtual machine اخر ، او كائن فى كمبيوتر فى الجهه الاخرى من العالم ، حيث يكون الكائن البعيد remote object قابل للوصول فى اى وقت من اى بيئه و يكون دائم persistent ، هذا النوع من ال Proxy صعب ترجمته فى بيئه الجافاسكربت لأن بيئه تشغيل جافاسكربت غير دائمه ، حيث يتم بدأ تطبيق الجافاسكربت و إنهائه قى كل طلب لصفحه تحتوى على كود جافاسكربت و عند مغادرتها ، كما انه لا يمكنك الوصول لتطبيق جافاسكربت اخر و التفاعل مع متغيراته بدون بدأه او انهائه فى البيئه الحاليه، لكن بالرغم من ذلك يمكنك إستخدام نمط التصميم proxy للوصول لكائنات مبنيه بلغات برمجه اخرى مثل php ، يمكن ان تكون خدمه ويب web service يتم التفاعل معها عن طريق جافاسكربت و التفاعل مع جميع وظائفها عن طريق كائن proxy .
ثالث نوع هو ال Protection Proxy ، هو ايضا صعب ترجمته لكود جافاسكربت ، لأنه يتححكم فى الوصول لوظائف الصنف من الاصناف الاخرى ، بحيث ان وظائف الكائن real subject لا يستطيع استخدامها الا اصناف معينه ، و هذا شبه مستحيل فى الجافاسكربت ، لانه لاتوجد طريقه لمعرفه من اى صنف صدر هذا الاستدعاء لوظيفه معينه من وظائف ال real subject ،وجب التنويه انه من خلال arguments.callee.caller داخل وظيفه ما ستعرف اى وظيفه قد قامت بإستدعاءها ، لكنك لن تستطيع معرفه الصنف الذى تنتمى له هذه الوظيفه .

مقارنه نمط التصميم Proxy بنمط التصميم Dcorator

كلا من نمط التصميم proxy و decorator يقوموا بإحاطه كائن بكائن اخر له نفس ال interface ، و يقوم الاثنين بتمرير الامر إلى الكائن الاصلى ، لكن الفرق يكمن فى وظيفه الكائن الذى يمثل النمط ، حيث يقوم الكائن الذى يمثل نمط التصميم decorator بتعديل الوظائف او اضافه وظائف جديده للكائن الذى يحيطه بدون انشاء اصناف فرعيه منه ، اما الكائن الذى يمثل ال proxy يقوم بتمرير الامر فقط بدون تغير الكائن الذى يحيطه ، هناك فرق اخر فى كيفيه انشاء الكائن الذى يحيطه النمط ، فى نمط التصميم decorator يتم انشاء الكائن component الذى يحيطه ال decorator منفردا ولا يعتمد على ال decorator اطلاقا ، اما فى نمط التصميم proxy يتم انشاء الكائن real subject من داخل ال proxy او يتم تخير انشاءه فى حاله virtual proxy عند الحاجه اليه فقط ، اى ان انشاء الكائن الذى يعمل عليه نمط التصميم proxy يعتمد اعتمادا كليا على الكائن proxy ، كما ان نمط التصميم decorator يمكن احاطه نفسه اكثر من مره ، اما نمط التصميم proxy لا يمكنه احاطه نفسه😀 .

مثال على Remote Proxy

لنفرض ان هناك خدمه ويب web service تقدم احصائيات عن اى صفحه على الانترنت مقدمه من keepondev مثل google statistics ، هذه الخدمه تحتوى على عدد من الروابط التى تمثل وظائف الكائنات التى بالسيرفر و كل الوظائف تقبل عبارات url و startDate و endDate ، لا يهم ما اللغه التى مكتوب بها الخدمه على السيرفر ، لان كل النتائح ستكون فى صيغه JSON ، هناك خمس وظائف فى خدمه الويب كما يمثلهم الروابط التاليه :

http://keepondev.com/stats/getPageViews/
http://keepondev.com/stats/getUniques/
http://keepondev.com/stats/getBrowserShare/
http://keepondev.com/stats/getTopSearchTerm/
http://keepondev.com/stats/getMostVisitedPages/

يمكننا برمجه ال StatsProxy فى صوره Singleton بحيث يكون له نفس ال interface كما الوظائف التى على السيرفر ، كما يوضح الكود البسيط التالى :

/* StatsProxy Singleton */
var StatsProxy = function(){
    /* private methods */
    var xhr = new XMLHttpRequest();
    var urls = {
        pageViews:"/stats/getPageViews/",
        uniques:"/stats/getUniques/",
        browserShare:"/stats/getBrowserShare/",
        topSearchTerms:"/stats/getTopSearchTerms/",
        mostVisitedPages:"/stats/getMostVisitedPages/",
    };

    function fetchData(url, dataCallback, startDate, endDate, page){
        var getVars = [];
        if(startDate != undefined){
            getVars.push("startDate="+ encodeURI(startDate));
        }
        if(endDate != undefined){
            getVars.push("endDate="+ encodeURI(endDate));
        }
        if(page != undefined){
            getVars.push("page="+ encodeURI(page));
        }

        if(getVars.length>0){
            url = url + "?" + getVars.join("&");
        }

        xhr.onreadystatechange = function(){
            if(xhr.readyState == 4){
                dataCallback(xhr.responseText);
            }
        };
        xhr.open("GET",url,true);
        xhr.send(null);
    };

    /* public methods */
    return {
        getPageViews:function(callback, startDate, endDate, page){
            fetchData(urls.pageViews, callback, startDate, endDate, page);
        },
        getUniques:function(callback, startDate, endDate, page){
            fetchData(urls.uniques, callback, startDate, endDate, page);
        },
        getBrowserShare:function(callback, startDate, endDate, page){
            fetchData(urls.browserShare, callback, startDate, endDate, page);
        },
        getTopSearchTerms:function(callback, startDate, endDate, page){
            fetchData(urls.topSearchTerms, callback, startDate, endDate, page);
        },
        getMostVisitedPages:function(callback, startDate, endDate, page){
            fetchData(urls.mostVisitedPages, callback, startDate, endDate, page);
        },
    }

}();

يمككننا الان معامله الكائن Stats الذى على السيرفر كأنه كائن عادى موجود فى بيئه المتصفح كما يوضح الكود البسيط التالى :

var stats = new StatsProxy();
stats.getPageViews(function(data){
    /* do something with data*/
},"01/01/08","01/01/09","index.html");

stats.getTopSearchTerms(function(){
    /* do something with data*/
},null,null,"products.html");

عليك ملاحظه ان البيانات التى ترجع من السيرفر فى صيغه JSON يتم تمريرها للوظيفه التى هى اول عباره للوظيفه getPageViews او getTopSearchTerms فى المثال السابق ، ميزه استخدام ال proxy فى المثال السابق اننا قد تمكنا من التعامل مع الكائن stats  كأنه كائن محلى و تفاصيل الروابط التى ينتج عنها الاحصائيات مخفيه لذلك يمكن تغيرها من نسخه لأخرى من اصدارات StatsProxy بدون التأثير على كود المستخدم .

مثال على Virtual proxy

لنفرض ان هناك كائن مكلف جدا فى انشاءه ، مثلا يقوم بتحميل كم كبير من البيانات من السيرفر عن طريق اجاكس او القيام بإنشاء العديد من الكائنات الاخرى التى تستهلك الكثير من موارد الذاكره ، من الافضل ان يتم انشاء هذا الكائن فقط عند الحاجه إليه ، يمكننا تحقيق ذلك عن طريق انشاء proxy يقوم بإحاطه الكائن الاصلى و لايقوم بإنشائه إلا عند استخدامه كإستدعاء احدى وظائف و اظهار رساله – جارى التحميل… – للمستخدم بدلا من مساحه خاليه توحى للمستخدم بإنه لايوجد شىء يعمل ، لنفرض ان الصنف HeavyLoadingClass يقوم بعمل اتصال اجاكس ليجلب كم هائل من البيانات فور انشاءه و بذلك يؤثر على كفائه التطبيق ، يمكننا عمل proxy يؤجل انشاء هذا الصنف فقط عند الحاجه إليه كما يوضح المثال البسيط التالى :

var HeavyLoadingClass = function(arg1, arg2){
    /*very heavy constructor*/
}
HeavyLoadingClass.prototype = {
    showData:function(data){
    /*show the data*/ 
    },
    hideData:function(){
    /*hide data container */
    }
}

var HeavyLoadingClassProxy = function(arg1, arg2){
    this._initialized = false;
}
HeavyLoadingClassProxy.prototype = {
    _initialize:function(){
        if(!this._initialized){
            this.class = new HeavyLoadingClass();
            this._initialized = true;
        }
    },
    showData:function(data){
        this._initialize();
        this.class.showData();
    },
    hideData:function(){
        this._initialize();
        this.class.hideData();
    },
}

الصنف HeavyLoadingClass مكلف فى إنشائه ، لذلك قمنا بعمل Proxy بإسم HeavyLoadingClassProxy الذى يؤجل انشاء الصنف المكلف الى الوقت الذى يتم إستخدامه فيه عن طريق استدعاء احدى وظائفه ، و بالتالى لن يتم انشاء هذا الصنف إلا عند الحاجه اليه ، هذا المثال بسيط جدا ، يمكنك الرجوع إلى كتاب Apress: JavaScript design patterns للمزيد .

فوائد و عيوب ال proxy

قد ذكرت فوائد إستخدام ال proxy فى نقطه “انوع ال Proxy” بأول الموضوع ، لكن عيوب ال remote proxy مثلا تتمثل فى ان استدعاء وظيفه من وظائف الكائن البعيد تتطلب اتصال اجاكس و الانتظار من اجل البيانات ثم اعربها ثم اعطائها لوظيفه لتعمل عليها ، قد يفترض المطور انها وظيفه عاديه يمكن الحصول على قيمه منها فورا بدون الانتظار او تسجيل وظيفه للعمل على البيانات الناتجه من السيرفر ، لكن من خلال التوثيق الجيد للكود الذى تكتبه يمكن للمطورين تجنب مثل هذه المشاكل ، اما virtual proxy فيقوم بإضافه طبقه منا التعقيد لكيفيه انشاء الكائنات و ماهو الحدث الذى سيؤدى الا انشاء الحدث … الخ ، و بالتالى سيصعب اكتشاف الاخطاء و معالجتها debugging ، لكن هذه العيوب لا تنفى ان لنمط التصميم proxy مميزات كبيره جدا اذا تم استخدامه بطريقه صحيحه و فى الظروف المناسبه .

هذا الموضوع تلخيص للفصل الرابع عشر فى كتاب Apress: Pro JavaScript Design Patterns ارجع للكتاب للمزيد ، الكتاب يحتوى على العديد من الامثله لم اذكرها و كيفيه انشاء virtual proxy ديناميكيا و كيفيه انشاء remote proxy قابل لإعاده الاستعمال لأكثر من كائن بعيد reomte object .

الأوسمة: , , , , , , ,

2 تعليقان to “proxy design pattern”

  1. غير معروف Says:

    عافاك الله يالغالي

  2. غير معروف Says:

    جميييييييييييييييييييييييييل

أضف تعليقاً

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

WordPress.com Logo

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

صورة تويتر

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

Facebook photo

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

Google+ photo

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

Connecting to %s


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