# Заместитель (Proxy)

## Заместитель (Proxy)

### Суть паттерна <a href="#intent" id="intent"></a>

**Заместитель** — это структурный паттерн проектирования, который позволяет подставлять вместо реальных объектов специальные объекты-заменители. Эти объекты перехватывают вызовы к оригинальному объекту, позволяя сделать что-то *до* или *после* передачи вызова оригиналу.

<figure><img src="https://refactoringguru.cn/images/patterns/content/proxy/proxy.png?id=efece4647fb11e3f7539291796327666" alt="Паттерн Заместитель" width="640"><figcaption></figcaption></figure>

### &#x20;Проблема <a href="#problem" id="problem"></a>

Для чего вообще контролировать доступ к объектам? Рассмотрим такой пример: у вас есть внешний ресурсоёмкий объект, который нужен не все время, а изредка.

<figure><img src="https://refactoringguru.cn/images/patterns/diagrams/proxy/problem-ru.png?id=06c6e7834de954b614b0c8ac366948a5" alt="Проблема, которую решает Заместитель" width="510"><figcaption><p>Запросы к базе данных могут быть очень медленными.</p></figcaption></figure>

Мы могли бы создавать этот объект не в самом начале программы, а только тогда, когда он кому-то реально понадобится. Каждый клиент объекта получил бы некий код отложенной инициализации. Но, вероятно, это привело бы к множественному дублированию кода.

В идеале, этот код хотелось бы поместить прямо в служебный класс, но это не всегда возможно. Например, код класса может находиться в закрытой сторонней библиотеке.

### &#x20;Решение <a href="#solution" id="solution"></a>

Паттерн Заместитель предлагает создать новый класс-дублёр, имеющий тот же интерфейс, что и оригинальный служебный объект. При получении запроса от клиента объект-заместитель сам бы создавал экземпляр служебного объекта и переадресовывал бы ему всю реальную работу.

<figure><img src="https://refactoringguru.cn/images/patterns/diagrams/proxy/solution-ru.png?id=3f0693eb00bf83b5bbd3ce99c1257e82" alt="Решение с помощью Заместителя" width="510"><figcaption><p>Заместитель «притворяется» базой данных, ускоряя работу за счёт ленивой инициализации и кеширования повторяющихся запросов.</p></figcaption></figure>

Но в чём же здесь польза? Вы могли бы поместить в класс заместителя какую-то промежуточную логику, которая выполнялась бы до (или после) вызовов этих же методов в настоящем объекте. А благодаря одинаковому интерфейсу, объект-заместитель можно передать в любой код, ожидающий сервисный объект.

### &#x20;Аналогия из жизни <a href="#analogy" id="analogy"></a>

<figure><img src="https://refactoringguru.cn/images/patterns/diagrams/proxy/live-example.png?id=a268c57fdaf073ee81cf4dfc7239eae2" alt="Платёжная карта — это заместитель пачки наличных" width="540"><figcaption><p>Платёжной картой можно расплачиваться, как и наличными.</p></figcaption></figure>

Платёжная карточка — это заместитель пачки наличных. И карточка, и наличные имеют общий интерфейс — ими можно оплачивать товары. Для покупателя польза в том, что не надо таскать с собой тонны наличных, а владелец магазина рад, что ему не нужно делать дорогостоящую инкассацию наличности в банк — деньги поступают к нему на счёт напрямую.

### &#x20;Структура <a href="#structure" id="structure"></a>

<figure><img src="https://refactoringguru.cn/images/patterns/diagrams/proxy/structure.png?id=f2478a82a84e1a1e512a8414bf1abd1c" alt="Структура классов паттерна Заместитель" width="370"><figcaption></figcaption></figure>

1. **Интерфейс сервиса** определяет общий интерфейс для сервиса и заместителя. Благодаря этому, объект заместителя можно использовать там, где ожидается объект сервиса.
2. **Сервис** содержит полезную бизнес-логику.
3. **Заместитель** хранит ссылку на объект сервиса. После того как заместитель заканчивает свою работу (например, инициализацию, логирование, защиту или другое), он передаёт вызовы вложенному сервису.

   Заместитель может сам отвечать за создание и удаление объекта сервиса.
4. **Клиент** работает с объектами через интерфейс сервиса. Благодаря этому, его можно «одурачить», подменив объект сервиса объектом заместителя.

### Применимость <a href="#applicability" id="applicability"></a>

&#x20;Ленивая инициализация (виртуальный прокси). Когда у вас есть тяжёлый объект, грузящий данные из файловой системы или базы данных.

&#x20;Вместо того, чтобы грузить данные сразу после старта программы, можно сэкономить ресурсы и создать объект тогда, когда он действительно понадобится.

&#x20;Защита доступа (защищающий прокси). Когда в программе есть разные типы пользователей, и вам хочется защищать объект от неавторизованного доступа. Например, если ваши объекты — это важная часть операционной системы, а пользователи — сторонние программы (хорошие или вредоносные).

&#x20;Прокси может проверять доступ при каждом вызове и передавать выполнение служебному объекту, если доступ разрешён.

&#x20;Локальный запуск сервиса (удалённый прокси). Когда настоящий сервисный объект находится на удалённом сервере.

&#x20;В этом случае заместитель транслирует запросы клиента в вызовы по сети в протоколе, понятном удалённому сервису.

&#x20;Логирование запросов (логирующий прокси). Когда требуется хранить историю обращений к сервисному объекту.

&#x20;Заместитель может сохранять историю обращения клиента к сервисному объекту.

&#x20;Кеширование объектов («умная» ссылка). Когда нужно кешировать результаты запросов клиентов и управлять их жизненным циклом.

&#x20;Заместитель может подсчитывать количество ссылок на сервисный объект, которые были отданы клиенту и остаются активными. Когда все ссылки освобождаются, можно будет освободить и сам сервисный объект (например, закрыть подключение к базе данных).

Кроме того, Заместитель может отслеживать, не менял ли клиент сервисный объект. Это позволит использовать объекты повторно и здóрово экономить ресурсы, особенно если речь идёт о больших прожорливых сервисах.

### &#x20;Шаги реализации <a href="#checklist" id="checklist"></a>

1. Определите интерфейс, который бы сделал заместитель и оригинальный объект взаимозаменяемыми.
2. Создайте класс заместителя. Он должен содержать ссылку на сервисный объект. Чаще всего, сервисный объект создаётся самим заместителем. В редких случаях заместитель получает готовый сервисный объект от клиента через конструктор.
3. Реализуйте методы заместителя в зависимости от его предназначения. В большинстве случаев, проделав какую-то полезную работу, методы заместителя должны передать запрос сервисному объекту.
4. Подумайте о введении фабрики, которая решала бы, какой из объектов создавать — заместитель или реальный сервисный объект. Но, с другой стороны, эта логика может быть помещена в создающий метод самого заместителя.
5. Подумайте, не реализовать ли вам ленивую инициализацию сервисного объекта при первом обращении клиента к методам заместителя.

### &#x20;Преимущества и недостатки <a href="#pros-cons" id="pros-cons"></a>

* &#x20;Позволяет контролировать сервисный объект незаметно для клиента.
* &#x20;Может работать, даже если сервисный объект ещё не создан.
* &#x20;Может контролировать жизненный цикл служебного объекта.
* &#x20;Усложняет код программы из-за введения дополнительных классов.
* &#x20;Увеличивает время отклика от сервиса.

## Пример

```cpp
# include <iostream>
# include <memory>
# include <map>
# include <random>

using namespace std;

class Subject
{
public:
        virtual ~Subject() = default;
        
        virtual pair<bool, double> request(size_t index) = 0;
        virtual bool changed() { return true; }
};

class RealSubject : public Subject
{
private:
        bool flag{ false };
        size_t counter{ 0 };

public:
        virtual pair<bool, double> request(size_t index) override;
        virtual bool changed() override;
};

class Proxy : public Subject
{
protected:
        shared_ptr<RealSubject> realsubject;

public:
        Proxy(shared_ptr<RealSubject> real) : realsubject(real) {}
};

class ConProxy : public Proxy
{
private:
        map<size_t, double> cache;

public:
        using Proxy::Proxy;

        virtual pair<bool, double> request(size_t index) override;
};

#pragma region Methods
bool RealSubject::changed()
{
        if (counter == 0)
        {
                flag = true;
        }
        if (++counter == 7)
        {
                counter = 0;
                flag = false;
        }
        return flag;
}


pair<bool, double> RealSubject::request(size_t index)
{
        random_device rd;
        mt19937 gen(rd());
        
        return pair<bool, double>(true, generate_canonical<double, 10>(gen));
}

pair<bool, double> ConProxy::request(size_t index)
{
        pair<bool, double> result;
        
        if (!realsubject)
        {
                cache.clear();
                
                result = pair<bool, double>(false, 0.);
        }
        else if (!realsubject->changed())
        {
                cache.clear();
                
                result = realsubject->request(index);
                
                cache.insert(map<size_t, double>::value_type(index, result.second));
        }
        else
        {
                map<size_t, double>::const_iterator it = cache.find(index);
        
                if (it != cache.end())
                {
                        result = pair<bool, double>(true, it->second);
                }
                else
                {
                        result = realsubject->request(index);
                
                        cache.insert(map<size_t, double>::value_type(index, result.second));
                }
        }
        
        return result;
}
#pragma endregion

int main()
{
        shared_ptr<RealSubject> subject = make_shared<RealSubject>();
        shared_ptr<Subject> proxy = make_shared<ConProxy>(subject);
        
        for (size_t i = 0; i < 21; ++i)
        {
                cout << "( " << i + 1 << ", " << proxy->request(i % 3).second << " )" << endl;
                
                if ((i + 1) % 3 == 0)
                        cout << endl;
        }
}

```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://frogs-organization-2.gitbook.io/oop.-sbornik./21.-strukturnye-patterny./zamestitel-proxy.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
