نمط الوكيل (Proxy)
نمط الوكيل (Proxy) هو نمط تصميم إنشائي يسمح لك بتوفير بديل لكائن موجود لديك، ويتحكم في الوصول إلى الكائن الأصلي، مما يمكنك من تنفيذ إجراءات قبل أو بعد وصول الطلب إلى الكائن الأصلي.
المشكلة
إليك مثالًا يوضح الحالة التي قد ترغب فيها بتقييد الوصول إلى إلى كائن ما، لنفرض أن لديك كائنًا كبيرًا جدًا يستهلك موارد النظام بشراهة، وأنت لا تريده أصلًا بشكل مستمر، بل من وقت لآخر.
ضع الصورة. استعلامات قاعدة البيانات قد تكون بطيئة جدًا. قد تستفيد من أسلوب البدء الكسول/المؤجَّل (Lazy Initialization) بأن تنشئ هذا الكائن عند الحاجة إليه فقط، وسيحتاج كل عملاء هذا الكائن أن ينفذوا شيفرة بدء مؤجل (Deferred Initialization) غير أننا سنخرج من هذا بشيفرات مكررة. وفي الحالات المثالية فإننا نريد أن نضع هذه الشيفرة مباشرة في فئة الكائن الخاص بنا، لكن هذا لا يمكن ضمانه في كل الحالات، ذلك أن الفئة قد تكون مثلًا جزءًا من مكتبة طرف ثالث مغلقة.
الحل
يقترح نمط الوكيل أن تنشئ فئة وكيل جديدة بنفس الواجهة التي يستخدمها كائن الخدمة (Service Object)، ثم تحدِّث تطبيقك ليمرِّر كائن الوكيل إلى كل عملاء الكائن الأصلي، وعند استلام طلب من عميل ينشئ الوكيلُ كائنَ خدمةٍ حقيقي ويفوض كل المهام إليه.
الصورة. يتخفى الوكيل في هيئة قاعدة بيانات، ويمكنه التعامل مع البدء الكسول وحفظ النتائج دون ملاحظة العميل أو قاعدة البيانات الحقيقية.
لكن ما الفائدة؟ إن كنت تحتاج إلى تنفيذ إجراء قبل أو بعد المنطق الأساسي للفئة (Primary Logic) فإن الوكيل يسمح لك بهذا دون تغيير تلك الفئة، وبما أنه -أي الوكيل- يستخدم نفس الواجهة كالفئة الأصلية، فيمكن تمريره إلى أي عميل يتوقع كائن خدمة حقيقي.
مثال واقعي
الصورة. يمكن استخدام بطاقات الائتمان للدفع مثل النقود الورقية تمامًا.
تمثل بطاقات الائتمان وكيلًا للحساب البنكي الذي يكون وكيلًا لكمية مالية، وكلاهما يستخدم نفس الواجهة، أنهما يُستخدمان للدفع مقابل المشتريات. وذلك يسعد العميل لا شك لأنه ليس عليه حمل أمواله كلما حل وارتحل، كما يسعد البائع أيضًا لأنه يقيه مخاطر السرقة أثناء نقل المال إلى البنك، أو مخاطر فقد المال داخل المتجر.
البنية
- تصرح فئة Service Interface عن واجهة للخدمة، ويجب أن يتبع الوكيل هذه الواجهة ليتمكن من التخفي في صورة كائن خدمة.
- فئة الخدمة Service هي فئة تعطي بعض المنطق التجاري المفيد.
- فئة Proxy لها حقل مرجعي يشير إلى كائن الخدمة، وبعد أن ينهي الوكيل عمليات معالجته (البدء الكسول والتسجيل والتحكم في الوصول والحفظ، إلخ) فإنه يمرر الطلب إلى كائن الخدمة، وعادة يدير الوكلاء دورة الحياة كاملة لكائنات الخدمة الخاصة بها.
- يجب أن تعمل فئة Client مع كل من الخدمات والوكلاء من خلال نفس الواجهة، وهكذا يمكنك تمرير وكيل إلى أي شيفرة تتوقع كائن خدمة.
مثال توضيحي
يبين هذا المثال كيف يمكن لنمط الوكيل Proxy أن يساهم في إدخال البدء الكسول (Lazy Initialization) والحفظ (Caching) إلى مكتبة تكامل من طرف ثالث ليوتيوب.
الصورة. نتائج حفظ خدمة مع وكيل (Proxy)
تزودنا المكتبة بفئة تحميل للفيديو، لكنها ليست بالكفاءة المطلوبة، ذلك أنه إن طلب تطبيق العميل نفس الفيديو أكثر من مرة فإن المكتبة تحمِّله من جديد في كل مرة بدلًا من حفظه وإعادة استخدام الملف المحفوظ. وتستخدم فئة الوكيل نفس الواجهة التي يستخدمها المحمِّل الأصلي، وتفوض إليها كل المهام، لكنها تتابع الملفات المحمَّلة وتعيد النتائج المحفوظة عندما يطلب التطبيق نفس الفيديو أكثر من مرة.
// (Remote Service) واجهة خدمة بعيدة.
interface ThirdPartyYoutubeLib is
method listVideos()
method getVideoInfo(id)
method downloadVideo(id)
// تستطيع أساليب هذه الفئة ،(Service Connector) الاستخدام الحقيقي لموصل الخدمة
// أن تطلب المعلومات من يوتيوب، وتعتمد سرعة الطلب على سرعة اتصال كل من المستخدم
// ويوتيوب بالإنترنت.
// سيبطئ أداء البرنامج إن حدثت طلبات كثيرة في نفس الوقت حتى لو طلبت كلها
// نقس البيانات.
class ThirdPartyYoutubeClass implements ThirdPartyYoutubeLib is
method listVideos() is
// إلى يوتيوب API أرسل طلب.
method getVideoInfo(id) is
// اجلب البيانات الوصفية لمقطع فيديو.
method downloadVideo(id) is
// حمِّل ملف فيديو من يوتيوب.
// نستطيع حفظ نتائج الطلب لبعض الوقت من أجل توفير البيانات، لكن قد يستحيل وضع
// شيفرة كهذه في فئة الخدمة، فقد تكون جزءًا من مكتبة طرف ثالث و-أو معرّفة لفئة
// لهذا نضع الشيفرة المحفوظة في فئة وكيل جديدة تستخدم نفس الواجهة (Final) نهائية
// التي تستخدمها فئة الخدمة، وهي تفوض المهام إلى كائن الخدمة عند إرسال الطلبات
// الحقيقية فقط.
class CachedYoutubeClass implements ThirdPartyYouTubeLib is
private field service: ThirdPartyYouTubeClass
private field listCache, videoCache
field needReset
constructor CachedYoutubeClass(service: ThirdPartyYouTubeLib) is
this.service = service
method listVideos() is
if (listCache == null || needReset)
listCache = service.listVideos()
return listCache
method getVideoInfo(id) is
if (videoCache == null || needReset)
videoCache = service.getVideoInfo(id)
return videoCache
method downloadVideo(id) is
if (!downloadExists(id) || needReset)
service.downloadVideo(id)
// تظل فئة الواجهة الرسومية كما هي دون تغيير طالما أنها تعمل مع كائن الخدمة من خلال
// واجهة، هذه الفئة كانت تعمل مباشرة مع كائن الخدمة.
// ونستطيع تمرير كائن وكيل بدلًا من كائن خدمة حقيقي بما أنهما يستخدمان نفس الواجهة.
class YoutubeManager is
protected field service: ThirdPartyYouTubeLib
constructor YoutubeManager(service: ThirdPartyYouTubeLib) is
this.service = service
method renderVideoPage(id) is
info = service.getVideoInfo(id)
// صفحة الفيديو (Render) أخرج.
method renderListPanel() is
list = service.listVideos()
// أخرج قائمة مصغَّرات الفيديو.
method reactOnUserInput() is
renderVideoPage()
renderListPanel()
// يستطيع البرنامج إعداد الوكلاء عند الحاجة.
class Application is
method init() is
aYouTubeService = new ThirdPartyYouTubeClass()
aYouTubeProxy = new CachedYouTubeClass(aYouTubeService)
manager = new YouTubeManager(aYouTubeProxy)
manager.reactOnUserInput()
قابلية التطبيق
توجد عشرات الاستخدامات لنمط الوكيل، إليك بعض أكثر هذه الاستخدامات شهرة:
- البدء الكسول (الوكيل الوهمي Virtual Proxy)، عندما يكون لديك كائن خدمة كبير الحجم يهدر موارد النظام بكونه نشطًا دائمًا رغم أنك لا تحتاجه إلا من وقت لآخر، فبدلًا من إنشاء الكائن عند فتح البرنامج، تستطيع تأخير بدء الكائن إلى حين الحاجة إليه.
تقييد الوصول (وكيل الحماية Protection Proxy)، حين تريد لعملاء بعينهم أن يستخدموا كائن الخدمة، كأن تمثل كائناتك أجزاءً مهمة من نظام تشغيل والعملاء هم برامج متعددة (بما في ذلك البرامج الخبيثة). ويمرر الوكيل الطلب إلى كائن الخدمة فقط في حالة إن كانت اعتماديات الوكيل (Proxy Credentials) تطابق بعض المعايير.
- التنفيذ المحلي لخدمة بعيدة (الوكيل البعيد Remote Proxy)، في حالة وجود كائن الخدمة على خادم بعيد، إذ في تلك الحالة يمرر الوكيلُ طلبَ العميلِ إلى الشبكة مسلِّمًا كل التفاصيل الخاصة بالعمل مع الشركة.
طلبات التسجيل (وكيل التسجيل Logging Proxy)، حين تريد أن تحافظ على تأريخ الطلبات الخاصة بكائن الخدمة. يستطيع الوكيل أن يسجل كل طلب قبل تمريره إلى الخدمة.
- حفظ نتائج الطلب (وكيل الحفظ Caching Proxy)، حين تحتاج أن تحفظ نتائج طلبات عميل وتدير دورة حياة هذه المحفوظات، خاصة إن كانت النتائج كبيرة جدًا. يستطيع الوكيل أن يستخدم الحفظ للطلبات المتكررة التي تخرج نفس النتائج دومًا، وقد يستخدم معامِلات الطلبات كمفاتيح للحفظ (Cache Keys).
الإشارة الذكية، حين تحتاج أن تكون قادرًا على فصل كائن كبير بمجرد أن تنتفي الحاجة إليه. يستطيع الوكيل أن يتابع العملاء الذين حصلوا على إشارة إلى كائن الخدمة أو نتائجه، وقد يتفقد الوكيلُ العملاءَ من وقت لآخر ليرى إن كانوا نشطاء أم لا. فإن فرغت قائمة العميل فإن الوكيل يفصل كائن الخدمة ويحرر موارد النظام التي كانت مخصصة له.
كذلك يستطيع الوكيل متابعة ما إن كان العميل قد عدَّل كائن الخدمة أم لا، ثم يمكن للعملاء الآخرين إعادة استخدام الكائنات التي لم تتغير.
كيفية الاستخدام
- إن لم تكن واجهة الخدمة موجودة فأنشئ واحدة لجعل الوكيل وكائنات الخدمة متبادلين (Interchangeable)، واعلم أن استخراج الواجهة من فئة الخدمة قد يكون غير ممكن أحيانًا، ذلك أنك ستحتاج إلى تغيير كل عملاء الخدمة لاستخدام تلك الواجهة، والخيار البديل لك هنا هو جعل الوكيل فئةً فرعية لفئة الخدمة كي يكتسب واجهتها.
- أنشئ فئة الوكيل، يجب أن تحتوي على حقل لتخزين مرجع إلى الخدمة، وعادة ينشئ الوكلاء خوادمهم ويديروا دورة حياتها كاملة، على أنه قد يُمرر خادم في حالات نادرة إلى الوكيل بواسطة العميل، ولكن من خلال منشئ (Constructor).
- استخدم أساليب الوكيل طبقًا لأغراضها، وفي أغلب الحالات، يجب أن يفوض الوكيل العمل إلى كائن الخدمة بعد تنفيذ بعض المهام.
- جرب إدخال أسلوبًا إنشائيًا يفرر ما إن كان العميل يحصل على وكيل أم خدمة حقيقية، ويمكن أن يكون هذا أسلوبًا ساكنًا بسيطًا في فئة الوكيل أو أسلوب مصنع كامل.
- جرب استخدام البدء الكسول في كائن الخدمة.
المزايا والعيوب
المزايا
- تستطيع التحكم في كائن الخدمة دون معرفة العملاء بذلك.
- تستطيع إدارة دورة حياة كائن الخدمة حين لا يهتم العملاء به.
- يعمل الوكيل حتى لو لم يكن كائن الخدمة جاهزًا أو كان غير متاح.
- مبدأ المفتوح/المغلق. تستطيع إدخال وكلاء جدد دون تغيير الخدمة أو العملاء.
العيوب
- قد تصبح الشيفرة معقدة بسبب حاجتك إلى إدخال فئات جديدة بكثرة.
- قد تتأخر استجابة الخدمة.
العلاقات مع الأنماط الأخرى
- يقدم نمط المحول (Adapter) واجهة مختلفة للكائن المغلَّف، بينما يزوده نمط الوكيل (Proxy) بنفس الواجهة، أما نمط المزخرف (Decorator) فيزوده بواجهة محسَّنة.
- تتشابه الواجهة والوكيل في أنهما يعالجان كيانًا معقدًا ويبدءانه بشكل مستقل. وعلى عكس نمط الواجهة فإن الوكيل له نفس واجهة كائن الخدمة الخاص به، ما يجعلهما متبادليْن (Interchangeable).
- يتشابه نمطا المزخرف والوكيل في بنيتيهما، لكن يختلفان في الغرض منهما، فكليهما بُنيا على مبدأ التركيب (Composition)، حيث يفترض بكائن ما أن يفوض بعض المهام إلى كائن آخر. والفرق بينهما هو أن الوكيل يدير دورة حياة كائن الخدمة الخاص به بنفسه، بينما يتحكم العميل دومًا في تركيب المزخرِفات.
الاستخدام في لغة جافا
المستوى: ★ ★ ☆
الانتشار: ★ ☆ ☆
أمثلة الاستخدام: قد لا يكون نمط الوكيل شائعًا في البرامج المكتوبة بلغة جافا، إلا أنه مفيد في بعض الحالات الخاصة، ولا يمكن استبداله حين تريد إضافة بعض السلوكيات إلى كائن من فئة ما دون تغيير شيفرة العميل. إليك بعض أمثلة النمط في مكتبات جافا:
- java.lang.reflect.Proxy
- *.java.rmi
- javax.ejb.EJB (انظر التعليقات)
- javax.inject.Inject (انظر التعليقات)
- javax.persistence.PersistenceContext
يمكن ملاحظة الوكلاء من خلال تفويضهم كل المهام الحقيقية إلى كائنات أخرى، وكل أسلوب وكيل يجب أن يشير في النهاية إلى كائن خدمة ما لم يكن الوكيل فئة فرعية لخدمة ما.
حفظ الوكيل
في هذا المثال ، يساهم نمط الوكيل في تطبيق البدء الكسول وحفظه إلى مكتبة تكامل ليوتيوب من طرف ثالث. ونمط الوكيل لا غنى عنه حين تريد إضافة بعض السلوكيات إلى فئة لا تستطيع التعديل على شيفرتها.
some_cool_media_library
some_cool_media_library/ThirdPartyYoutubeLib.java: واجهة الخدمة البعيدة
package refactoring_guru.proxy.example.some_cool_media_library;
import java.util.HashMap;
public interface ThirdPartyYoutubeLib {
HashMap<String, Video> popularVideos();
Video getVideo(String videoId);
}
some_cool_media_library/ThirdPartyYoutubeClass.java: استخدام الخدمة البعيدة
package refactoring_guru.proxy.example.some_cool_media_library;
import java.util.HashMap;
public class ThirdPartyYoutubeClass implements ThirdPartyYoutubeLib {
@Override
public HashMap<String, Video> popularVideos() {
connectToServer("http://www.youtube.com");
return getRandomVideos();
}
@Override
public Video getVideo(String videoId) {
connectToServer("http://www.youtube.com/" + videoId);
return getSomeVideo(videoId);
}
// -----------------------------------------------------------------------
// أساليب وهمية لمحاكاة نشاط الشبكة، سرعة هذه الأساليب بطيئة جدًا.
private int random(int min, int max) {
return min + (int) (Math.random() * ((max - min) + 1));
}
private void experienceNetworkLatency() {
int randomLatency = random(5, 10);
for (int i = 0; i < randomLatency; i++) {
try {
Thread.sleep(100);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
}
private void connectToServer(String server) {
System.out.print("Connecting to " + server + "... ");
experienceNetworkLatency();
System.out.print("Connected!" + "\n");
}
private HashMap<String, Video> getRandomVideos() {
System.out.print("Downloading populars... ");
experienceNetworkLatency();
HashMap<String, Video> hmap = new HashMap<String, Video>();
hmap.put("catzzzzzzzzz", new Video("sadgahasgdas", "Catzzzz.avi"));
hmap.put("mkafksangasj", new Video("mkafksangasj", "Dog play with ball.mp4"));
hmap.put("dancesvideoo", new Video("asdfas3ffasd", "Dancing video.mpq"));
hmap.put("dlsdk5jfslaf", new Video("dlsdk5jfslaf", "Barcelona vs RealM.mov"));
hmap.put("3sdfgsd1j333", new Video("3sdfgsd1j333", "Programing lesson#1.avi"));
System.out.print("Done!" + "\n");
return hmap;
}
private Video getSomeVideo(String videoId) {
System.out.print("Downloading video... ");
experienceNetworkLatency();
Video video = new Video(videoId, "Some video title");
System.out.print("Done!" + "\n");
return video;
}
}
some_cool_media_library/Video.java: ملف فيديو
package refactoring_guru.proxy.example.some_cool_media_library;
public class Video {
public String id;
public String title;
public String data;
Video(String id, String title) {
this.id = id;
this.title = title;
this.data = "Random video.";
}
}
الوكيل
proxy/YoutubeCacheProxy.java: حفظ الوكيل
package refactoring_guru.proxy.example.proxy;
import refactoring_guru.proxy.example.some_cool_media_library.ThirdPartyYoutubeClass;
import refactoring_guru.proxy.example.some_cool_media_library.ThirdPartyYoutubeLib;
import refactoring_guru.proxy.example.some_cool_media_library.Video;
import java.util.HashMap;
public class YoutubeCacheProxy implements ThirdPartyYoutubeLib {
private ThirdPartyYoutubeLib youtubeService;
private HashMap<String, Video> cachePopular = new HashMap<String, Video>();
private HashMap<String, Video> cacheAll = new HashMap<String, Video>();
public YoutubeCacheProxy() {
this.youtubeService = new ThirdPartyYoutubeClass();
}
@Override
public HashMap<String, Video> popularVideos() {
if (cachePopular.isEmpty()) {
cachePopular = youtubeService.popularVideos();
} else {
System.out.println("Retrieved list from cache.");
}
return cachePopular;
}
@Override
public Video getVideo(String videoId) {
Video video = cacheAll.get(videoId);
if (video == null) {
video = youtubeService.getVideo(videoId);
cacheAll.put(videoId, video);
} else {
System.out.println("Retrieved video '" + videoId + "' from cache.");
}
return video;
}
public void reset() {
cachePopular.clear();
cacheAll.clear();
}
}
المحمِّل
downloader/YoutubeDownloader.java: تطبيق تحميل الوسائط
package refactoring_guru.proxy.example.downloader;
import refactoring_guru.proxy.example.some_cool_media_library.ThirdPartyYoutubeLib;
import refactoring_guru.proxy.example.some_cool_media_library.Video;
import java.util.HashMap;
public class YoutubeDownloader {
private ThirdPartyYoutubeLib api;
public YoutubeDownloader(ThirdPartyYoutubeLib api) {
this.api = api;
}
public void renderVideoPage(String videoId) {
Video video = api.getVideo(videoId);
System.out.println("\n-------------------------------");
System.out.println("Video page (imagine fancy HTML)");
System.out.println("ID: " + video.id);
System.out.println("Title: " + video.title);
System.out.println("Video: " + video.data);
System.out.println("-------------------------------\n");
}
public void renderPopularVideos() {
HashMap<String, Video> list = api.popularVideos();
System.out.println("\n-------------------------------");
System.out.println("Most popular videos on Youtube (imagine fancy HTML)");
for (Video video : list.values()) {
System.out.println("ID: " + video.id + " / Title: " + video.title);
}
System.out.println("-------------------------------\n");
}
}
Demo.java: شيفرة البدء
package refactoring_guru.proxy.example;
import refactoring_guru.proxy.example.downloader.YoutubeDownloader;
import refactoring_guru.proxy.example.proxy.YoutubeCacheProxy;
import refactoring_guru.proxy.example.some_cool_media_library.ThirdPartyYoutubeClass;
public class Demo {
public static void main(String[] args) {
YoutubeDownloader naiveDownloader = new YoutubeDownloader(new ThirdPartyYoutubeClass());
YoutubeDownloader smartDownloader = new YoutubeDownloader(new YoutubeCacheProxy());
long naive = test(naiveDownloader);
long smart = test(smartDownloader);
System.out.print("Time saved by caching proxy: " + (naive - smart) + "ms");
}
private static long test(YoutubeDownloader downloader) {
long startTime = System.currentTimeMillis();
// سلوك المستخدم في تطبيقنا:
downloader.renderPopularVideos();
downloader.renderVideoPage("catzzzzzzzzz");
downloader.renderPopularVideos();
downloader.renderVideoPage("dancesvideoo");
// قد يزور المستخدمون نفس الصفحة كثيرًا.
downloader.renderVideoPage("catzzzzzzzzz");
downloader.renderVideoPage("someothervid");
long estimatedTime = System.currentTimeMillis() - startTime;
System.out.print("Time elapsed: " + estimatedTime + "ms\n");
return estimatedTime;
}
}
OutputDemo.txt: نتائج التنفيذ
Connecting to http://www.youtube.com... Connected!
Downloading populars... Done!
-------------------------------
Most popular videos on Youtube (imagine fancy HTML)
ID: sadgahasgdas / Title: Catzzzz.avi
ID: asdfas3ffasd / Title: Dancing video.mpq
ID: 3sdfgsd1j333 / Title: Programing lesson#1.avi
ID: mkafksangasj / Title: Dog play with ball.mp4
ID: dlsdk5jfslaf / Title: Barcelona vs RealM.mov
-------------------------------
Connecting to http://www.youtube.com/catzzzzzzzzz... Connected!
Downloading video... Done!
-------------------------------
Video page (imagine fancy HTML)
ID: catzzzzzzzzz
Title: Some video title
Video: Random video.
-------------------------------
Connecting to http://www.youtube.com... Connected!
Downloading populars... Done!
-------------------------------
Most popular videos on Youtube (imagine fancy HTML)
ID: sadgahasgdas / Title: Catzzzz.avi
ID: asdfas3ffasd / Title: Dancing video.mpq
ID: 3sdfgsd1j333 / Title: Programing lesson#1.avi
ID: mkafksangasj / Title: Dog play with ball.mp4
ID: dlsdk5jfslaf / Title: Barcelona vs RealM.mov
-------------------------------
Connecting to http://www.youtube.com/dancesvideoo... Connected!
Downloading video... Done!
-------------------------------
Video page (imagine fancy HTML)
ID: dancesvideoo
Title: Some video title
Video: Random video.
-------------------------------
Connecting to http://www.youtube.com/catzzzzzzzzz... Connected!
Downloading video... Done!
-------------------------------
Video page (imagine fancy HTML)
ID: catzzzzzzzzz
Title: Some video title
Video: Random video.
-------------------------------
Connecting to http://www.youtube.com/someothervid... Connected!
Downloading video... Done!
-------------------------------
Video page (imagine fancy HTML)
ID: someothervid
Title: Some video title
Video: Random video.
-------------------------------
Time elapsed: 9354ms
Connecting to http://www.youtube.com... Connected!
Downloading populars... Done!
-------------------------------
Most popular videos on Youtube (imagine fancy HTML)
ID: sadgahasgdas / Title: Catzzzz.avi
ID: asdfas3ffasd / Title: Dancing video.mpq
ID: 3sdfgsd1j333 / Title: Programing lesson#1.avi
ID: mkafksangasj / Title: Dog play with ball.mp4
ID: dlsdk5jfslaf / Title: Barcelona vs RealM.mov
-------------------------------
Connecting to http://www.youtube.com/catzzzzzzzzz... Connected!
Downloading video... Done!
-------------------------------
Video page (imagine fancy HTML)
ID: catzzzzzzzzz
Title: Some video title
Video: Random video.
-------------------------------
Retrieved list from cache.
-------------------------------
Most popular videos on Youtube (imagine fancy HTML)
ID: sadgahasgdas / Title: Catzzzz.avi
ID: asdfas3ffasd / Title: Dancing video.mpq
ID: 3sdfgsd1j333 / Title: Programing lesson#1.avi
ID: mkafksangasj / Title: Dog play with ball.mp4
ID: dlsdk5jfslaf / Title: Barcelona vs RealM.mov
-------------------------------
Connecting to http://www.youtube.com/dancesvideoo... Connected!
Downloading video... Done!
-------------------------------
Video page (imagine fancy HTML)
ID: dancesvideoo
Title: Some video title
Video: Random video.
-------------------------------
Retrieved video 'catzzzzzzzzz' from cache.
-------------------------------
Video page (imagine fancy HTML)
ID: catzzzzzzzzz
Title: Some video title
Video: Random video.
-------------------------------
Connecting to http://www.youtube.com/someothervid... Connected!
Downloading video... Done!
-------------------------------
Video page (imagine fancy HTML)
ID: someothervid
Title: Some video title
Video: Random video.
-------------------------------
Time elapsed: 5875ms
Time saved by caching proxy: 3479ms
الاستخدام في لغة #C
المستوى: ★ ★ ☆
الانتشار: ★ ☆ ☆
أمثلة الاستخدام: قد لا يكون نمط الوكيل شائعًا في البرامج المكتوبة بلغة جافا، إلا أنه مفيد في بعض الحالات الخاصة، ولا يمكن استبداله حين تريد إضافة بعض السلوكيات إلى كائن من فئة ما دون تغيير شيفرة العميل.
يمكن ملاحظة الوكلاء من خلال تفويضهم كل المهام الحقيقية إلى كائنات أخرى، وكل أسلوب وكيل يجب أن يشير في النهاية إلى كائن خدمة ما لم يكن الوكيل فئة فرعية لخدمة ما.
مثال تصوري
يوضح هذا المثال بنية نمط الوكيل، ويركز على إجابة الأسئلة التالية:
- ما الفئات التي يتكون منها؟
- ما الأدوار التي تلعبها هذه الفئات؟
- كيف ترتبط عناصر النمط ببعضها؟
Program.cs: مثال تصوري
using System;
namespace RefactoringGuru.DesignPatterns.Proxy.Conceptual
{
// RealSubject تصرح واجهة الكائن عن العمليات المشتركة لكل من فئة
// والوكيل، وستستطيع تمرير وكيل بدلًا من كائن حقيقي إلى العميل طالما
// باستخدام هذه الواجهة RealSubject يعمل العميل مع.
public interface ISubject
{
void Request();
}
// على بعض المنطق التجاري، وعادة تستطيع فئات RealSubject تحتوي فئة
// أن تنفذ بعض المهام المفيدة التي قد تكون بطيئة أو حساسة RealSubject
// كتصحيح بيانات الإدخال مثلًا، ويستطيع الوكيل حل هذه المشاكل دون أي
// RealSubject تغييرات في شيفرة فئة.
class RealSubject : ISubject
{
public void Request()
{
Console.WriteLine("RealSubject: Handling Request.");
}
}
// RealSubject للوكيل واجهة مطابقة لواجهة فئة.
class Proxy : ISubject
{
private RealSubject _realSubject;
public Proxy(RealSubject realSubject)
{
this._realSubject = realSubject;
}
// (Lazy Loading) أكثر استخدامات الوكيل هي التحميل الكسول
// وتقييد الوصول والتسجيل، إلخ (Caching) والحفظ.
// يستطيع الوكيل تنفيذ أحد هذه الاستخدامات ثم يمرر التنفيذ إلى نفس الأسلوب
// وذلك وفقًا لنتيجة تنفيذ ذلك الأسلوب ، RealSubject في كائن مرتبط به من فئة.
public void Request()
{
if (this.CheckAccess())
{
this._realSubject = new RealSubject();
this._realSubject.Request();
this.LogAccess();
}
}
public bool CheckAccess()
{
// ينبغي أن تُجرى تحققات من الوصول هنا.
Console.WriteLine("Proxy: Checking access prior to firing a real request.");
return true;
}
public void LogAccess()
{
Console.WriteLine("Proxy: Logging the time of request.");
}
}
public class Client
{
// (Subjects) يفترض بشيفرة العميل أن تعمل مع كل الكائنات، سواء التوابع
// من أجل دعم التوابع الحقيقية والوكلاء ، (Subject) أو الوكلاء، من خلال واجهة التابع
// لكن العملاء في الحياة الواقعية يعملون مع توابعهم الحقيقية مباشرة،
// ولتسهيل استخدام النمط في تلك الحالة، يمكنك توسيع وكيلك من فئة
// (Real Subject) التابع الحقيقي
public void ClientCode(ISubject subject)
{
// ...
subject.Request();
// ...
}
}
class Program
{
static void Main(string[] args)
{
Client client = new Client();
Console.WriteLine("Client: Executing the client code with a real subject:");
RealSubject realSubject = new RealSubject();
client.ClientCode(realSubject);
Console.WriteLine();
Console.WriteLine("Client: Executing the same client code with a proxy:");
Proxy proxy = new Proxy(realSubject);
client.ClientCode(proxy);
}
}
}
Output.txt: نتائج التنفيذ
Client: Executing the client code with a real subject:
RealSubject: Handling Request.
Client: Executing the same client code with a proxy:
Proxy: Checking access prior to firing a real request.
RealSubject: Handling Request.
Proxy: Logging the time of request.
الاستخدام في لغة PHP
المستوى: ★ ★ ☆
الانتشار: ★ ☆ ☆
أمثلة الاستخدام:
الاستخدام في لغة بايثون
المستوى: ★ ★ ☆
الانتشار: ★ ☆ ☆
أمثلة الاستخدام:
الاستخدام في لغة Ruby
المستوى: ★ ★ ☆
الانتشار: ★ ☆ ☆
أمثلة الاستخدام:
الاستخدام في لغة Swift
المستوى: ★ ★ ☆
الانتشار: ★ ☆ ☆
أمثلة الاستخدام:
الاستخدام في لغة TypeScript
المستوى: ★ ★ ☆
الانتشار: ★ ☆ ☆
أمثلة الاستخدام: