---
title: "الوكيل"
type: "design-pattern"
slug: "proxy"
url: "http://localhost:3000/ar/design-patterns/proxy.md"
category: "الأنماط الهيكلية"
description: "الوكيل (Proxy) هو نمط تصميم هيكلي يتيح لك توفير بديل أو عنصر نائب لكائن آخر. يتحكم الوكيل في الوصول إلى الكائن الأصلي، مما يسمح لك بتنفيذ شيء ما إما قبل وصول الطلب إلى الكائن الأصلي أو بعده."
languages: ["java", "csharp", "cpp", "go", "php", "python", "ruby", "rust", "swift", "typescript"]
---
# الوكيل

> الوكيل (Proxy) هو نمط تصميم هيكلي يتيح لك توفير بديل أو عنصر نائب لكائن آخر. يتحكم الوكيل في الوصول إلى الكائن الأصلي، مما يسمح لك بتنفيذ شيء ما إما قبل وصول الطلب إلى الكائن الأصلي أو بعده.

## Intent

**الوكيل** هو نمط تصميم هيكلي يتيح لك توفير بديل أو عنصر نائب لكائن آخر. يتحكم الوكيل في الوصول إلى الكائن الأصلي، مما يسمح لك بتنفيذ شيء ما إما قبل وصول الطلب إلى الكائن الأصلي أو بعده.

## Problem

لماذا تريد التحكم في الوصول إلى كائن ما؟ إليك مثال: لديك كائن ضخم يستهلك قدراً هائلاً من موارد النظام. تحتاجه من وقت لآخر، لكن ليس دائماً.

استعلامات قاعدة البيانات يمكن أن تكون بطيئة جداً.

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

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

## Solution

يقترح نمط الوكيل إنشاء فئة وكيل جديدة بنفس واجهة كائن الخدمة الأصلي. ثم تُحدّث تطبيقك ليمرر كائن الوكيل إلى جميع عملاء الكائن الأصلي. عند تلقي طلب من عميل، يُنشئ الوكيل كائن خدمة حقيقياً ويفوض إليه جميع الأعمال.

يتنكر الوكيل كأنه كائن قاعدة بيانات. يمكنه التعامل مع التهيئة الكسولة وتخزين النتائج مؤقتاً دون أن يعلم العميل أو كائن قاعدة البيانات الحقيقي بذلك.

لكن ما هي الفائدة؟ إذا كنت تحتاج إلى تنفيذ شيء ما إما قبل المنطق الأساسي للفئة أو بعده، يسمح لك الوكيل بفعل ذلك دون تغيير تلك الفئة. بما أن الوكيل يُنفذ نفس واجهة الفئة الأصلية، يمكن تمريره إلى أي عميل يتوقع كائن خدمة حقيقياً.

## Structure

1. تُعلن **واجهة الخدمة** عن واجهة الخدمة. يجب على الوكيل اتباع هذه الواجهة ليتمكن من التنكر كـكائن خدمة.
2. **الخدمة** هي فئة تُقدم بعض منطق الأعمال المفيد.
3. تحتوي فئة **الوكيل** على حقل مرجعي يشير إلى كائن الخدمة. بعد أن ينهي الوكيل معالجته (مثل: التهيئة الكسولة، التسجيل، التحكم في الوصول، التخزين المؤقت، إلخ.)، يمرر الطلب إلى كائن الخدمة.
عادةً ما يدير الوكلاء دورة الحياة الكاملة لكائنات الخدمة الخاصة بهم.
4. يجب على **العميل** التعامل مع كل من الخدمات والوكلاء عبر نفس الواجهة. بهذه الطريقة يمكنك تمرير وكيل إلى أي كود يتوقع كائن خدمة.

## Pseudocode

يوضح هذا المثال كيف يمكن لنمط **الوكيل** المساعدة في تقديم التهيئة الكسولة والتخزين المؤقت لمكتبة تكامل YouTube من طرف ثالث.

تخزين نتائج الخدمة مؤقتاً باستخدام الوكيل.

The library provides us with the video downloading class. However, it’s very inefficient. If the client application requests the same video multiple times, the library just downloads it over and over, instead of caching and reusing the first downloaded file.

تُنفذ فئة الوكيل نفس الواجهة التي ينفذها المُنزِّل الأصلي وتفوض إليه جميع الأعمال. ومع ذلك، تتتبع الملفات المحملة وتُعيد النتيجة المخزنة مؤقتاً عندما يطلب التطبيق الفيديو ذاته عدة مرات.

// واجهة خدمة بعيدة.
interface ThirdPartyYouTubeLib is
    method listVideos()
    method getVideoInfo(id)
    method downloadVideo(id)

// التطبيق الملموس لموصّل الخدمة. يمكن لطرق
// هذه الفئة طلب معلومات من YouTube. تعتمد سرعة
// الطلب على اتصال المستخدم بالإنترنت
// وكذلك على YouTube. سيبطؤ التطبيق إذا أُرسلت كثير من
// الطلبات في نفس الوقت، حتى وإن طلبت جميعها
// نفس المعلومات.
class ThirdPartyYouTubeClass implements ThirdPartyYouTubeLib is
    method listVideos() is
        // إرسال طلب API إلى YouTube.

    method getVideoInfo(id) is
        // الحصول على بيانات وصفية لمقطع فيديو.

    method downloadVideo(id) is
        // تنزيل ملف فيديو من YouTube.

// لتوفير عرض النطاق الترددي، يمكننا تخزين نتائج الطلبات مؤقتاً والاحتفاظ
// بها لبعض الوقت. لكن قد يكون من المستحيل وضع هذا الكود
// مباشرةً في فئة الخدمة. على سبيل المثال، ربما تم
// تقديمه كجزء من مكتبة طرف ثالث و/أو تعريفه
// بـ `final`. لهذا السبب نضع كود التخزين المؤقت في
// فئة وكيل جديدة تُنفذ نفس الواجهة التي تُنفذها
// فئة الخدمة. تفوض إلى كائن الخدمة فقط عندما
// يجب إرسال الطلبات الحقيقية.
class CachedYouTubeClass implements ThirdPartyYouTubeLib is
    private field service: ThirdPartyYouTubeLib
    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)

// تبقى فئة واجهة المستخدم الرسومية (GUI)، التي كانت تعمل مباشرةً مع
// كائن خدمة، دون تغيير طالما تعمل مع كائن الخدمة
// عبر واجهة. يمكننا بأمان تمرير كائن وكيل
// بدلاً من كائن خدمة حقيقي لأن كليهما
// يُنفذ نفس الواجهة.
class YouTubeManager is
    protected field service: ThirdPartyYouTubeLib

    constructor YouTubeManager(service: ThirdPartyYouTubeLib) is
        this.service = service

    method renderVideoPage(id) is
        info = service.getVideoInfo(id)
        // عرض صفحة الفيديو.

    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()

## Applicability

هناك عشرات الطرق لاستخدام نمط الوكيل. دعنا نستعرض أكثر الاستخدامات شيوعاً.

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

 بدلاً من إنشاء الكائن عند تشغيل التطبيق، يمكنك تأجيل تهيئة الكائن إلى الوقت الذي يُحتاج إليه فعلاً.

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

 يمكن للوكيل تمرير الطلب إلى كائن الخدمة فقط إذا تطابقت بيانات اعتماد العميل مع معايير معينة.

 التنفيذ المحلي لخدمة بعيدة (الوكيل البعيد). يحدث هذا عندما يكون كائن الخدمة موجوداً على خادم بعيد.

 في هذه الحالة، يُمرر الوكيل طلب العميل عبر الشبكة، متولياً معالجة جميع التفاصيل المعقدة للعمل مع الشبكة.

 تسجيل الطلبات (وكيل التسجيل). يحدث هذا عندما تريد الاحتفاظ بسجل للطلبات الموجهة إلى كائن الخدمة.

 يمكن للوكيل تسجيل كل طلب قبل تمريره إلى الخدمة.

 تخزين نتائج الطلبات مؤقتاً (وكيل التخزين المؤقت). يحدث هذا عندما تحتاج إلى تخزين نتائج طلبات العملاء مؤقتاً وإدارة دورة حياة هذا التخزين، خاصةً إذا كانت النتائج كبيرة الحجم.

 يمكن للوكيل تنفيذ التخزين المؤقت للطلبات المتكررة التي تُنتج دائماً نفس النتائج. قد يستخدم الوكيل معاملات الطلبات كمفاتيح للتخزين المؤقت.

 المرجع الذكي. يحدث هذا عندما تحتاج إلى إمكانية إلغاء كائن ثقيل الوزن بمجرد عدم وجود عملاء يستخدمونه.

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

يمكن للوكيل أيضاً تتبع ما إذا كان العميل قد عدّل كائن الخدمة. عندها يمكن إعادة استخدام الكائنات غير المعدلة من قِبل عملاء آخرين.

## How to Implement

1. إذا لم تكن هناك واجهة خدمة موجودة مسبقاً، أنشئ واحدة لجعل كائنات الوكيل والخدمة قابلة للتبادل. استخراج الواجهة من فئة الخدمة ليس ممكناً دائماً، لأنك ستحتاج إلى تغيير جميع عملاء الخدمة لاستخدام تلك الواجهة. الخطة البديلة هي جعل الوكيل فئة فرعية من فئة الخدمة، وبذلك سيرث واجهة الخدمة.
2. أنشئ فئة الوكيل. يجب أن تحتوي على حقل لتخزين مرجع للخدمة. عادةً ما ينشئ الوكلاء دورة حياة خدماتهم بالكامل ويديرونها. في حالات نادرة، يتم تمرير الخدمة إلى الوكيل عبر المُنشئ من قِبل العميل.
3. نفّذ أساليب الوكيل وفقاً لأغراضها. في معظم الحالات، بعد تنفيذ بعض الأعمال، يجب على الوكيل تفويض العمل إلى كائن الخدمة.
4. فكّر في تقديم أسلوب إنشاء يقرر ما إذا كان العميل سيحصل على وكيل أو خدمة حقيقية. يمكن أن يكون هذا أسلوباً ثابتاً بسيطاً في فئة الوكيل أو أسلوب مصنع متكامل.
5. فكّر في تطبيق التهيئة الكسولة لكائن الخدمة.

## Pros

* يمكنك التحكم في كائن الخدمة دون أن يعلم العملاء بذلك.
* يمكنك إدارة دورة حياة كائن الخدمة عندما لا يهتم العملاء بها.
* يعمل الوكيل حتى لو لم يكن كائن الخدمة جاهزاً أو غير متاح.
* _مبدأ الفتح/الإغلاق_. يمكنك تقديم وكلاء جدد دون تغيير الخدمة أو العملاء.

## Cons

* قد يصبح الكود أكثر تعقيداً نظراً لحاجتك إلى تقديم الكثير من الفئات الجديدة.
* قد يتأخر الرد من الخدمة.

## Relations with Other Patterns

* مع [المحوِّل (Adapter)](/ar/design-patterns/adapter) تصل إلى كائن موجود عبر واجهة مختلفة. مع [الوكيل (Proxy)](/ar/design-patterns/proxy)، تبقى الواجهة كما هي. مع [المزيِّن (Decorator)](/ar/design-patterns/decorator) تصل إلى الكائن عبر واجهة معزّزة.
* [الواجهة الخارجية (Facade)](/ar/design-patterns/facade) مشابهة لـ [الوكيل (Proxy)](/ar/design-patterns/proxy) في أن كليهما يُخزّن كياناً معقداً ويُهيئه بمفرده. على عكس _الواجهة الخارجية_، يمتلك _الوكيل_ نفس واجهة كائن خدمته، مما يجعلهما قابلَين للتبادل.
* يمتلك [المزيِّن (Decorator)](/ar/design-patterns/decorator) و[الوكيل (Proxy)](/ar/design-patterns/proxy) هياكل مشابهة، لكن أهدافاً مختلفة جداً. كلا النمطين مبنيان على مبدأ التركيب، حيث من المفترض أن يفوّض كائن ما بعض العمل إلى كائن آخر. الفرق هو أن _الوكيل_ يدير عادةً دورة حياة كائن خدمته بشكل مستقل، في حين أن تركيب _المزيِّنات_ يكون دائماً تحت سيطرة العميل.
## Relations

**Related patterns**

- [المهايئ (Adapter)](/ar/design-patterns/adapter.md)
- [المُزَخرِف](/ar/design-patterns/decorator.md)
- [الواجهة (Facade)](/ar/design-patterns/facade.md)

## Code Examples

### java

```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);
}

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;
    }

}

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.";
    }
}

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();
    }
}

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");
    }
}

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;
    }
}

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
```

### csharp

```csharp
using System;

namespace RefactoringGuru.DesignPatterns.Proxy.Conceptual
{
    // تُعلن واجهة Subject عمليات مشتركة لكل من RealSubject
    // و Proxy. طالما يتعامل العميل مع RealSubject باستخدام هذه
    // الواجهة، ستتمكن من تمرير وكيل بدلاً من الموضوع الحقيقي.
    public interface ISubject
    {
        void Request();
    }
    
    // يحتوي RealSubject على منطق أعمال أساسي. عادةً تكون RealSubjects
    // قادرة على تنفيذ أعمال مفيدة قد تكون بطيئة جداً أو
    // حساسة - مثل تصحيح البيانات المدخلة. يمكن للوكيل حل هذه المشكلات
    // دون إجراء أي تغييرات على كود 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;
        }
        
        // أكثر تطبيقات نمط الوكيل شيوعاً هي: التحميل الكسول،
        // التخزين المؤقت، التحكم في الوصول، التسجيل، إلخ. يمكن للوكيل
        // تنفيذ إحدى هذه المهام ثم، بناءً على النتيجة، تمرير
        // التنفيذ إلى نفس الطريقة في كائن RealSubject المرتبط.
        public void Request()
        {
            if (this.CheckAccess())
            {
                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
    {
        // من المفترض أن يعمل كود العميل مع جميع الكائنات (الموضوعات
        // والوكلاء) عبر واجهة 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);
        }
    }
}

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.
```

### cpp

```cpp
#include <iostream>
/**
 * تُعلن واجهة Subject عمليات مشتركة لكل من RealSubject و
 * Proxy. طالما يتعامل العميل مع RealSubject باستخدام هذه الواجهة،
 * ستتمكن من تمرير وكيل بدلاً من الموضوع الحقيقي.
 */
class Subject {
 public:
  virtual void Request() const = 0;
};
/**
 * يحتوي RealSubject على منطق أعمال أساسي. عادةً تكون RealSubjects
 * قادرة على تنفيذ أعمال مفيدة قد تكون بطيئة جداً أو حساسة -
 * مثل تصحيح البيانات المدخلة. يمكن للوكيل حل هذه المشكلات دون
 * إجراء أي تغييرات على كود RealSubject.
 */
class RealSubject : public Subject {
 public:
  void Request() const override {
    std::cout << "RealSubject: Handling request.\n";
  }
};
/**
 * للوكيل واجهة مطابقة لـ RealSubject.
 */
class Proxy : public Subject {
  /**
   * @var RealSubject
   */
 private:
  RealSubject *real_subject_;

  bool CheckAccess() const {
    // يجب وضع فحوصات حقيقية هنا.
    std::cout << "Proxy: Checking access prior to firing a real request.\n";
    return true;
  }
  void LogAccess() const {
    std::cout << "Proxy: Logging the time of request.\n";
  }

  /**
   * يحتفظ الوكيل بمرجع لكائن من فئة RealSubject. يمكن أن يكون
   * محمَّلاً بشكل كسول أو ممرَّراً إلى الوكيل من قِبل العميل.
   */
 public:
  Proxy(RealSubject *real_subject) : real_subject_(new RealSubject(*real_subject)) {
  }

  ~Proxy() {
    delete real_subject_;
  }
  /**
   * أكثر تطبيقات نمط الوكيل شيوعاً هي: التحميل الكسول،
   * التخزين المؤقت، التحكم في الوصول، التسجيل، إلخ. يمكن للوكيل تنفيذ إحدى
   * هذه المهام ثم، بناءً على النتيجة، تمرير التنفيذ إلى
   * نفس الطريقة في كائن RealSubject المرتبط.
   */
  void Request() const override {
    if (this->CheckAccess()) {
      this->real_subject_->Request();
      this->LogAccess();
    }
  }
};
/**
 * من المفترض أن يعمل كود العميل مع جميع الكائنات (الموضوعات والوكلاء)
 * عبر واجهة Subject لدعم كل من الموضوعات الحقيقية والوكلاء.
 * في الواقع، يعمل العملاء في الغالب مع موضوعاتهم الحقيقية
 * مباشرةً. في هذه الحالة، لتطبيق النمط بسهولة أكبر، يمكنك
 * توسيع وكيلك من فئة الموضوع الحقيقي.
 */
void ClientCode(const Subject &subject) {
  // ...
  subject.Request();
  // ...
}

int main() {
  std::cout << "Client: Executing the client code with a real subject:\n";
  RealSubject *real_subject = new RealSubject;
  ClientCode(*real_subject);
  std::cout << "\n";
  std::cout << "Client: Executing the same client code with a proxy:\n";
  Proxy *proxy = new Proxy(real_subject);
  ClientCode(*proxy);

  delete real_subject;
  delete proxy;
  return 0;
}

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.
```

### go

```go
package main

type server interface {
	handleRequest(string, string) (int, string)
}

package main

type Nginx struct {
	application       *Application
	maxAllowedRequest int
	rateLimiter       map[string]int
}

func newNginxServer() *Nginx {
	return &Nginx{
		application:       &Application{},
		maxAllowedRequest: 2,
		rateLimiter:       make(map[string]int),
	}
}

func (n *Nginx) handleRequest(url, method string) (int, string) {
	allowed := n.checkRateLimiting(url)
	if !allowed {
		return 403, "Not Allowed"
	}
	return n.application.handleRequest(url, method)
}

func (n *Nginx) checkRateLimiting(url string) bool {
	if n.rateLimiter[url] == 0 {
		n.rateLimiter[url] = 1
	}
	if n.rateLimiter[url] > n.maxAllowedRequest {
		return false
	}
	n.rateLimiter[url] = n.rateLimiter[url] + 1
	return true
}

package main

type Application struct {
}

func (a *Application) handleRequest(url, method string) (int, string) {
	if url == "/app/status" && method == "GET" {
		return 200, "Ok"
	}

	if url == "/create/user" && method == "POST" {
		return 201, "User Created"
	}
	return 404, "Not Ok"
}

package main

import "fmt"

func main() {

	nginxServer := newNginxServer()
	appStatusURL := "/app/status"
	createuserURL := "/create/user"

	httpCode, body := nginxServer.handleRequest(appStatusURL, "GET")
	fmt.Printf("\nUrl: %s\nHttpCode: %d\nBody: %s\n", appStatusURL, httpCode, body)

	httpCode, body = nginxServer.handleRequest(appStatusURL, "GET")
	fmt.Printf("\nUrl: %s\nHttpCode: %d\nBody: %s\n", appStatusURL, httpCode, body)

	httpCode, body = nginxServer.handleRequest(appStatusURL, "GET")
	fmt.Printf("\nUrl: %s\nHttpCode: %d\nBody: %s\n", appStatusURL, httpCode, body)

	httpCode, body = nginxServer.handleRequest(createuserURL, "POST")
	fmt.Printf("\nUrl: %s\nHttpCode: %d\nBody: %s\n", appStatusURL, httpCode, body)

	httpCode, body = nginxServer.handleRequest(createuserURL, "GET")
	fmt.Printf("\nUrl: %s\nHttpCode: %d\nBody: %s\n", appStatusURL, httpCode, body)
}

Url: /app/status
HttpCode: 200
Body: Ok

Url: /app/status
HttpCode: 200
Body: Ok

Url: /app/status
HttpCode: 403
Body: Not Allowed

Url: /app/status
HttpCode: 201
Body: User Created

Url: /app/status
HttpCode: 404
Body: Not Ok
```

### php

```php
<?php

namespace RefactoringGuru\Proxy\Conceptual;

/**
 * تُعلن واجهة Subject عمليات مشتركة لكل من RealSubject و
 * Proxy. طالما يتعامل العميل مع RealSubject باستخدام هذه الواجهة،
 * ستتمكن من تمرير وكيل بدلاً من الموضوع الحقيقي.
 */
interface Subject
{
    public function request(): void;
}

/**
 * يحتوي RealSubject على منطق أعمال أساسي. عادةً تكون RealSubjects
 * قادرة على تنفيذ أعمال مفيدة قد تكون بطيئة جداً أو حساسة -
 * مثل تصحيح البيانات المدخلة. يمكن للوكيل حل هذه المشكلات دون
 * إجراء أي تغييرات على كود RealSubject.
 */
class RealSubject implements Subject
{
    public function request(): void
    {
        echo "RealSubject: Handling request.\n";
    }
}

/**
 * للوكيل واجهة مطابقة لـ RealSubject.
 */
class Proxy implements Subject
{
    /**
     * @var RealSubject
     */
    private $realSubject;

    /**
     * يحتفظ الوكيل بمرجع لكائن من فئة RealSubject. يمكن أن يكون
     * محمَّلاً بشكل كسول أو ممرَّراً إلى الوكيل من قِبل العميل.
     */
    public function __construct(RealSubject $realSubject)
    {
        $this->realSubject = $realSubject;
    }

    /**
     * أكثر تطبيقات نمط الوكيل شيوعاً هي: التحميل الكسول،
     * التخزين المؤقت، التحكم في الوصول، التسجيل، إلخ. يمكن للوكيل تنفيذ إحدى
     * هذه المهام ثم، بناءً على النتيجة، تمرير التنفيذ إلى
     * نفس الطريقة في كائن RealSubject المرتبط.
     */
    public function request(): void
    {
        if ($this->checkAccess()) {
            $this->realSubject->request();
            $this->logAccess();
        }
    }

    private function checkAccess(): bool
    {
        // يجب وضع فحوصات حقيقية هنا.
        echo "Proxy: Checking access prior to firing a real request.\n";

        return true;
    }

    private function logAccess(): void
    {
        echo "Proxy: Logging the time of request.\n";
    }
}

/**
 * من المفترض أن يعمل كود العميل مع جميع الكائنات (الموضوعات والوكلاء)
 * عبر واجهة Subject لدعم كل من الموضوعات الحقيقية والوكلاء.
 * في الواقع، يعمل العملاء في الغالب مع موضوعاتهم الحقيقية
 * مباشرةً. في هذه الحالة، لتطبيق النمط بسهولة أكبر، يمكنك
 * توسيع وكيلك من فئة الموضوع الحقيقي.
 */
function clientCode(Subject $subject)
{
    // ...

    $subject->request();

    // ...
}

echo "Client: Executing the client code with a real subject:\n";
$realSubject = new RealSubject();
clientCode($realSubject);

echo "\n";

echo "Client: Executing the same client code with a proxy:\n";
$proxy = new Proxy($realSubject);
clientCode($proxy);

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

namespace RefactoringGuru\Proxy\RealWorld;

/**
 * تصف واجهة Subject واجهة الكائن الحقيقي.
 *
 * الحقيقة أن كثيراً من التطبيقات الحقيقية قد لا تملك هذه الواجهة معرّفة بوضوح.
 * إذا كنت في هذا الموقف، فأفضل خياراتك هو توسيع الوكيل من إحدى
 * فئات تطبيقك الموجودة. إذا كان ذلك محرجاً، فينبغي أن يكون استخراج
 * واجهة مناسبة خطوتك الأولى.
 */
interface Downloader
{
    public function download(string $url): string;
}

/**
 * يقوم الموضوع الحقيقي بالعمل الفعلي، وإن لم يكن بالطريقة الأكثر كفاءة.
 * عندما يحاول عميل تنزيل الملف ذاته للمرة الثانية، يفعل المُنزِّل
 * ذلك بدلاً من جلب النتيجة من التخزين المؤقت.
 */
class SimpleDownloader implements Downloader
{
    public function download(string $url): string
    {
        echo "Downloading a file from the Internet.\n";
        $result = file_get_contents($url);
        echo "Downloaded bytes: " . strlen($result) . "\n";

        return $result;
    }
}

/**
 * فئة الوكيل هي محاولتنا لجعل التنزيل أكثر كفاءة. تُغلّف
 * كائن المُنزِّل الحقيقي وتفوض إليه أول طلبات التنزيل. يُخزَّن
 * الناتج مؤقتاً بعد ذلك، مما يجعل الطلبات اللاحقة تُعيد ملفاً موجوداً
 * بدلاً من تنزيله مجدداً.
 *
 * لاحظ أن الوكيل يجب أن ينفّذ نفس الواجهة التي ينفّذها الموضوع الحقيقي.
 */
class CachingDownloader implements Downloader
{
    /**
     * @var SimpleDownloader
     */
    private $downloader;

    /**
     * @var string[]
     */
    private $cache = [];

    public function __construct(SimpleDownloader $downloader)
    {
        $this->downloader = $downloader;
    }

    public function download(string $url): string
    {
        if (!isset($this->cache[$url])) {
            echo "CacheProxy MISS. ";
            $result = $this->downloader->download($url);
            $this->cache[$url] = $result;
        } else {
            echo "CacheProxy HIT. Retrieving result from cache.\n";
        }
        return $this->cache[$url];
    }
}

/**
 * قد يُصدر كود العميل عدة طلبات تنزيل متشابهة. في هذه الحالة،
 * يوفر وكيل التخزين المؤقت الوقت والبيانات بتقديم النتائج من التخزين المؤقت.
 *
 * العميل لا يعلم أنه يعمل مع وكيل لأنه يتعامل مع المُنزِّلات
 * عبر الواجهة المجردة.
 */
function clientCode(Downloader $subject)
{
    // ...

    $result = $subject->download("http://example.com/");

    // يمكن تخزين طلبات التنزيل المكررة مؤقتاً لتحسين السرعة.

    $result = $subject->download("http://example.com/");

    // ...
}

echo "Executing client code with real subject:\n";
$realSubject = new SimpleDownloader();
clientCode($realSubject);

echo "\n";

echo "Executing the same client code with a proxy:\n";
$proxy = new CachingDownloader($realSubject);
clientCode($proxy);

Executing client code with real subject:
Downloading a file from the Internet.
Downloaded bytes: 1270
Downloading a file from the Internet.
Downloaded bytes: 1270

Executing the same client code with a proxy:
CacheProxy MISS. Downloading a file from the Internet.
Downloaded bytes: 1270
CacheProxy HIT. Retrieving result from cache.
```

### python

```python
from abc import ABC, abstractmethod


class Subject(ABC):
    """
    تُعلن واجهة Subject عمليات مشتركة لكل من RealSubject و
    Proxy. طالما يتعامل العميل مع RealSubject باستخدام هذه
    الواجهة، ستتمكن من تمرير وكيل بدلاً من الموضوع الحقيقي.
    """

    @abstractmethod
    def request(self) -> None:
        pass


class RealSubject(Subject):
    """
    يحتوي RealSubject على منطق أعمال أساسي. عادةً تكون RealSubjects
    قادرة على تنفيذ أعمال مفيدة قد تكون بطيئة جداً أو حساسة -
    مثل تصحيح البيانات المدخلة. يمكن للوكيل حل هذه المشكلات دون
    إجراء أي تغييرات على كود RealSubject.
    """

    def request(self) -> None:
        print("RealSubject: Handling request.")


class Proxy(Subject):
    """
    للوكيل واجهة مطابقة لـ RealSubject.
    """

    def __init__(self, real_subject: RealSubject) -> None:
        self._real_subject = real_subject

    def request(self) -> None:
        """
        The most common applications of the Proxy pattern are lazy loading,
        caching, controlling the access, logging, etc. A Proxy can perform one
        of these things and then, depending on the result, pass the execution to
        the same method in a linked RealSubject object.
        """

        if self.check_access():
            self._real_subject.request()
            self.log_access()

    def check_access(self) -> bool:
        print("Proxy: Checking access prior to firing a real request.")
        return True

    def log_access(self) -> None:
        print("Proxy: Logging the time of request.", end="")


def client_code(subject: Subject) -> None:
    """
    The client code is supposed to work with all objects (both subjects and
    proxies) via the Subject interface in order to support both real subjects
    and proxies. In real life, however, clients mostly work with their real
    subjects directly. In this case, to implement the pattern more easily, you
    can extend your proxy from the real subject's class.
    """

    # ...

    subject.request()

    # ...


if __name__ == "__main__":
    print("Client: Executing the client code with a real subject:")
    real_subject = RealSubject()
    client_code(real_subject)

    print("")

    print("Client: Executing the same client code with a proxy:")
    proxy = Proxy(real_subject)
    client_code(proxy)

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.
```

### ruby

```ruby
# تُعلن واجهة Subject عمليات مشتركة لكل من RealSubject و
# Proxy. طالما يتعامل العميل مع RealSubject باستخدام هذه الواجهة،
# ستتمكن من تمرير وكيل بدلاً من الموضوع الحقيقي.
class Subject
  # @abstract
  def request
    raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'"
  end
end

# يحتوي RealSubject على منطق أعمال أساسي. عادةً تكون RealSubjects
# قادرة على تنفيذ أعمال مفيدة قد تكون بطيئة جداً أو حساسة -
# مثل تصحيح البيانات المدخلة. يمكن للوكيل حل هذه المشكلات دون
# إجراء أي تغييرات على كود RealSubject.
class RealSubject < Subject
  def request
    puts 'RealSubject: Handling request.'
  end
end

# للوكيل واجهة مطابقة لـ RealSubject.
class Proxy < Subject
  # @param [RealSubject] real_subject
  def initialize(real_subject)
    @real_subject = real_subject
  end

  # أكثر تطبيقات نمط الوكيل شيوعاً هي: التحميل الكسول، التخزين المؤقت،
  # التحكم في الوصول، التسجيل، إلخ. يمكن للوكيل تنفيذ إحدى هذه
  # المهام ثم، بناءً على النتيجة، تمرير التنفيذ إلى نفس
  # الطريقة في كائن RealSubject المرتبط.
  def request
    return unless check_access

    @real_subject.request
    log_access
  end

  # @return [Boolean]
  def check_access
    puts 'Proxy: Checking access prior to firing a real request.'
    true
  end

  def log_access
    print 'Proxy: Logging the time of request.'
  end
end

# من المفترض أن يعمل كود العميل مع جميع الكائنات (الموضوعات والوكلاء)
# عبر واجهة Subject لدعم كل من الموضوعات الحقيقية والوكلاء.
# في الواقع، يعمل العملاء في الغالب مع موضوعاتهم الحقيقية
# مباشرةً. في هذه الحالة، لتطبيق النمط بسهولة أكبر، يمكنك
# توسيع وكيلك من فئة الموضوع الحقيقي.
def client_code(subject)
  # ...

  subject.request

  # ...
end

puts 'Client: Executing the client code with a real subject:'
real_subject = RealSubject.new
client_code(real_subject)

puts "\n"

puts 'Client: Executing the same client code with a proxy:'
proxy = Proxy.new(real_subject)
client_code(proxy)

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.
```

### rust

```rust
mod application;
mod nginx;

pub use nginx::NginxServer;

pub trait Server {
    fn handle_request(&mut self, url: &str, method: &str) -> (u16, String);
}

use super::Server;

pub struct Application;

impl Server for Application {
    fn handle_request(&mut self, url: &str, method: &str) -> (u16, String) {
        if url == "/app/status" && method == "GET" {
            return (200, "Ok".into());
        }

        if url == "/create/user" && method == "POST" {
            return (201, "User Created".into());
        }

        (404, "Not Ok".into())
    }
}

use std::collections::HashMap;

use super::{application::Application, Server};

/// خادم NGINX هو وكيل لخادم التطبيق.
pub struct NginxServer {
    application: Application,
    max_allowed_requests: u32,
    rate_limiter: HashMap<String, u32>,
}

impl NginxServer {
    pub fn new() -> Self {
        Self {
            application: Application,
            max_allowed_requests: 2,
            rate_limiter: HashMap::default(),
        }
    }

    pub fn check_rate_limiting(&mut self, url: &str) -> bool {
        let rate = self.rate_limiter.entry(url.to_string()).or_insert(1);

        if *rate > self.max_allowed_requests {
            return false;
        }

        *rate += 1;
        true
    }
}

impl Server for NginxServer {
    fn handle_request(&mut self, url: &str, method: &str) -> (u16, String) {
        if !self.check_rate_limiting(url) {
            return (403, "Not Allowed".into());
        }

        self.application.handle_request(url, method)
    }
}

mod server;

use crate::server::{NginxServer, Server};

fn main() {
    let app_status = &"/app/status".to_string();
    let create_user = &"/create/user".to_string();

    let mut nginx = NginxServer::new();

    let (code, body) = nginx.handle_request(app_status, "GET");
    println!("Url: {}\nHttpCode: {}\nBody: {}\n", app_status, code, body);

    let (code, body) = nginx.handle_request(app_status, "GET");
    println!("Url: {}\nHttpCode: {}\nBody: {}\n", app_status, code, body);

    let (code, body) = nginx.handle_request(app_status, "GET");
    println!("Url: {}\nHttpCode: {}\nBody: {}\n", app_status, code, body);

    let (code, body) = nginx.handle_request(create_user, "POST");
    println!("Url: {}\nHttpCode: {}\nBody: {}\n", create_user, code, body);

    let (code, body) = nginx.handle_request(create_user, "GET");
    println!("Url: {}\nHttpCode: {}\nBody: {}\n", create_user, code, body);
}

Url: /app/status
HttpCode: 200
Body: Ok

Url: /app/status
HttpCode: 200
Body: Ok

Url: /app/status
HttpCode: 403
Body: Not Allowed

Url: /create/user
HttpCode: 201
Body: User Created

Url: /create/user
HttpCode: 404
Body: Not Ok
```

### swift

```swift
import XCTest

/// تُعلن واجهة Subject عمليات مشتركة لكل من RealSubject و
/// Proxy. طالما يتعامل العميل مع RealSubject باستخدام هذه
/// الواجهة، ستتمكن من تمرير وكيل بدلاً من الموضوع الحقيقي.
protocol Subject {

    func request()
}

/// يحتوي RealSubject على منطق أعمال أساسي. عادةً تكون RealSubjects
/// قادرة على تنفيذ أعمال مفيدة قد تكون بطيئة جداً أو حساسة -
/// مثل تصحيح البيانات المدخلة. يمكن للوكيل حل هذه المشكلات دون
/// إجراء أي تغييرات على كود RealSubject.
class RealSubject: Subject {

    func request() {
        print("RealSubject: Handling request.")
    }
}

/// للوكيل واجهة مطابقة لـ RealSubject.
class Proxy: Subject {

    private var realSubject: RealSubject

    /// يحتفظ الوكيل بمرجع لكائن من فئة RealSubject.
    /// يمكن أن يكون محمَّلاً بشكل كسول أو ممرَّراً إلى الوكيل من قِبل العميل.
    init(_ realSubject: RealSubject) {
        self.realSubject = realSubject
    }

    /// أكثر تطبيقات نمط الوكيل شيوعاً هي: التحميل الكسول،
    /// التخزين المؤقت، التحكم في الوصول، التسجيل، إلخ. يمكن للوكيل تنفيذ إحدى
    /// هذه المهام ثم، بناءً على النتيجة، تمرير التنفيذ إلى
    /// نفس الطريقة في كائن RealSubject المرتبط.
    func request() {

        if (checkAccess()) {
            realSubject.request()
            logAccess()
        }
    }

    private func checkAccess() -> Bool {

        /// يجب وضع فحوصات حقيقية هنا.

        print("Proxy: Checking access prior to firing a real request.")

        return true
    }

    private func logAccess() {
        print("Proxy: Logging the time of request.")
    }
}

/// من المفترض أن يعمل كود العميل مع جميع الكائنات (الموضوعات والوكلاء)
/// عبر واجهة Subject لدعم كل من الموضوعات الحقيقية والوكلاء.
/// في الواقع، يعمل العملاء في الغالب مع موضوعاتهم الحقيقية
/// مباشرةً. في هذه الحالة، لتطبيق النمط بسهولة أكبر، يمكنك
/// توسيع وكيلك من فئة الموضوع الحقيقي.
class Client {
    // ...
    static func clientCode(subject: Subject) {
        // ...
        subject.request()
        // ...
    }
    // ...
}

/// لنرَ كيف يعمل كل شيء معاً.
class ProxyConceptual: XCTestCase {

    func test() {
        print("Client: Executing the client code with a real subject:")
        let realSubject = RealSubject()
        Client.clientCode(subject: realSubject)

        print("\nClient: Executing the same client code with a proxy:")
        let proxy = Proxy(realSubject)
        Client.clientCode(subject: proxy)
    }
}

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.

import XCTest

class ProxyRealWorld: XCTestCase {

    /// نمط تصميم الوكيل
    ///
    /// النية: توفير بديل أو عنصر نائب لكائن آخر للتحكم
    /// في الوصول إلى الكائن الأصلي أو لإضافة مسؤوليات أخرى.
    ///
    /// مثال: هناك طرق لا حصر لها لاستخدام الوكلاء: التخزين المؤقت، التسجيل،
    /// التحكم في الوصول، التهيئة المؤجلة، إلخ.

    func testProxyRealWorld() {

        print("Client: Loading a profile WITHOUT proxy")
        loadBasicProfile(with: Keychain())
        loadProfileWithBankAccount(with: Keychain())

        print("\nClient: Let's load a profile WITH proxy")
        loadBasicProfile(with: ProfileProxy())
        loadProfileWithBankAccount(with: ProfileProxy())
    }

    func loadBasicProfile(with service: ProfileService) {

        service.loadProfile(with: [.basic], success: { profile in
            print("Client: Basic profile is loaded")
        }) { error in
            print("Client: Cannot load a basic profile")
            print("Client: Error: " + error.localizedSummary)
        }
    }

    func loadProfileWithBankAccount(with service: ProfileService) {

        service.loadProfile(with: [.basic, .bankAccount], success: { profile in
            print("Client: Basic profile with a bank account is loaded")
        }) { error in
            print("Client: Cannot load a profile with a bank account")
            print("Client: Error: " + error.localizedSummary)
        }
    }
}

enum AccessField {

    case basic
    case bankAccount
}

protocol ProfileService {

    typealias Success = (Profile) -> ()
    typealias Failure = (LocalizedError) -> ()

    func loadProfile(with fields: [AccessField], success: Success, failure: Failure)
}

class ProfileProxy: ProfileService {

    private let keychain = Keychain()

    func loadProfile(with fields: [AccessField], success: Success, failure: Failure) {

        if let error = checkAccess(for: fields) {
            failure(error)
        } else {
            /// ملاحظة:
            /// في هذه المرحلة، يمكن تمرير الإغلاقات `success` و`failure`
            /// مباشرةً إلى الخدمة الأصلية (كما هو الحال الآن) أو
            /// توسيعها هنا للتعامل مع نتيجة (مثلاً، للتخزين المؤقت).

            keychain.loadProfile(with: fields, success: success, failure: failure)
        }
    }

    private func checkAccess(for fields: [AccessField]) -> LocalizedError? {
        if fields.contains(.bankAccount) {
            switch BiometricsService.checkAccess() {
            case .authorized: return nil
            case .denied: return ProfileError.accessDenied
            }
        }
        return nil
    }
}

class Keychain: ProfileService {

    func loadProfile(with fields: [AccessField], success: Success, failure: Failure) {

        var profile = Profile()

        for item in fields {
            switch item {
            case .basic:
                let info = loadBasicProfile()
                profile.firstName = info[Profile.Keys.firstName.raw]
                profile.lastName = info[Profile.Keys.lastName.raw]
                profile.email = info[Profile.Keys.email.raw]
            case .bankAccount:
                profile.bankAccount = loadBankAccount()
            }
        }

        success(profile)
    }

    private func loadBasicProfile() -> [String : String] {
        /// يجلب هذه الحقول من مخزن آمن.
        return [Profile.Keys.firstName.raw : "Vasya",
                Profile.Keys.lastName.raw : "Pupkin",
                Profile.Keys.email.raw : "vasya.pupkin@gmail.com"]
    }

    private func loadBankAccount() -> BankAccount {
        /// يجلب هذه الحقول من مخزن آمن.
        return BankAccount(id: 12345, amount: 999)
    }
}

class BiometricsService {

    enum Access {
        case authorized
        case denied
    }

    static func checkAccess() -> Access {
        /// تستخدم الخدمة Face ID أو Touch ID أو كلمة مرور عادية
        /// لتحديد ما إذا كان المستخدم الحالي هو مالك الجهاز.

        /// لنفترض في مثالنا أن المستخدم نسي كلمة المرور :)
        return .denied
    }
}

struct Profile {

    enum Keys: String {
        case firstName
        case lastName
        case email
    }

    var firstName: String?
    var lastName: String?
    var email: String?

    var bankAccount: BankAccount?
}

struct BankAccount {

    var id: Int
    var amount: Double
}

enum ProfileError: LocalizedError {

    case accessDenied

    var errorDescription: String? {
        switch self {
        case .accessDenied:
            return "Access is denied. Please enter a valid password"
        }
    }
}

extension RawRepresentable {

    var raw: Self.RawValue {
        return rawValue
    }
}

extension LocalizedError {

    var localizedSummary: String {
        return errorDescription ?? ""
    }
}

Client: Loading a profile WITHOUT proxy
Client: Basic profile is loaded
Client: Basic profile with a bank account is loaded

Client: Let's load a profile WITH proxy
Client: Basic profile is loaded
Client: Cannot load a profile with a bank account
Client: Error: Access is denied. Please enter a valid password
```

### typescript

```typescript
/**
 * تُعلن واجهة Subject عمليات مشتركة لكل من RealSubject و
 * Proxy. طالما يتعامل العميل مع RealSubject باستخدام هذه الواجهة،
 * ستتمكن من تمرير وكيل بدلاً من الموضوع الحقيقي.
 */
interface Subject {
    request(): void;
}

/**
 * يحتوي RealSubject على منطق أعمال أساسي. عادةً تكون RealSubjects
 * قادرة على تنفيذ أعمال مفيدة قد تكون بطيئة جداً أو حساسة -
 * مثل تصحيح البيانات المدخلة. يمكن للوكيل حل هذه المشكلات دون
 * إجراء أي تغييرات على كود RealSubject.
 */
class RealSubject implements Subject {
    public request(): void {
        console.log('RealSubject: Handling request.');
    }
}

/**
 * للوكيل واجهة مطابقة لـ RealSubject.
 */
class Proxy implements Subject {
    private realSubject: RealSubject;

    /**
     * يحتفظ الوكيل بمرجع لكائن من فئة RealSubject. يمكن أن يكون
     * محمَّلاً بشكل كسول أو ممرَّراً إلى الوكيل من قِبل العميل.
     */
    constructor(realSubject: RealSubject) {
        this.realSubject = realSubject;
    }

    /**
     * أكثر تطبيقات نمط الوكيل شيوعاً هي: التحميل الكسول،
     * التخزين المؤقت، التحكم في الوصول، التسجيل، إلخ. يمكن للوكيل تنفيذ إحدى
     * هذه المهام ثم، بناءً على النتيجة، تمرير التنفيذ إلى
     * نفس الطريقة في كائن RealSubject المرتبط.
     */
    public request(): void {
        if (this.checkAccess()) {
            this.realSubject.request();
            this.logAccess();
        }
    }

    private checkAccess(): boolean {
        // يجب وضع فحوصات حقيقية هنا.
        console.log('Proxy: Checking access prior to firing a real request.');

        return true;
    }

    private logAccess(): void {
        console.log('Proxy: Logging the time of request.');
    }
}

/**
 * من المفترض أن يعمل كود العميل مع جميع الكائنات (الموضوعات والوكلاء)
 * عبر واجهة Subject لدعم كل من الموضوعات الحقيقية والوكلاء.
 * في الواقع، يعمل العملاء في الغالب مع موضوعاتهم الحقيقية
 * مباشرةً. في هذه الحالة، لتطبيق النمط بسهولة أكبر، يمكنك
 * توسيع وكيلك من فئة الموضوع الحقيقي.
 */
function clientCode(subject: Subject) {
    // ...

    subject.request();

    // ...
}

console.log('Client: Executing the client code with a real subject:');
const realSubject = new RealSubject();
clientCode(realSubject);

console.log('');

console.log('Client: Executing the same client code with a proxy:');
const proxy = new Proxy(realSubject);
clientCode(proxy);

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.
```

