> For the complete documentation index, see [llms.txt](https://frogs-organization-2.gitbook.io/oop.-sbornik./llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://frogs-organization-2.gitbook.io/oop.-sbornik./21.-strukturnye-patterny./zamestitel-proxy.md).

# Заместитель (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;
        }
}

```
