> 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./adapter-adapter.md).

# Адаптер (Adapter)

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

**Адаптер** — это структурный паттерн проектирования, который позволяет объектам с несовместимыми интерфейсами работать вместе.

<figure><img src="https://refactoringguru.cn/images/patterns/content/adapter/adapter-en.png?id=11ef6ae6177291834323e3f918c47cd2" alt="Паттерн Адаптер" width="640"><figcaption></figcaption></figure>

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

Представьте, что вы делаете приложение для торговли на бирже. Ваше приложение скачивает биржевые котировки из нескольких источников в XML, а затем рисует красивые графики.

В какой-то момент вы решаете улучшить приложение, применив стороннюю библиотеку аналитики. Но вот беда — библиотека поддерживает только формат данных JSON, несовместимый с вашим приложением.

<figure><img src="https://refactoringguru.cn/images/patterns/diagrams/adapter/problem-en.png?id=60d01f6c72ba85030cd52d5955caa3d8" alt="Структура программы до подключения сторонней библиотеки" width="530"><figcaption><p>Подключить стороннюю библиотеку не выйдет из-за несовместимых форматов данных.</p></figcaption></figure>

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

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

Вы можете создать *адаптер*. Это объект-переводчик, который трансформирует интерфейс или данные одного объекта в такой вид, чтобы он стал понятен другому объекту.

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

Адаптеры могут не только переводить данные из одного формата в другой, но и помогать объектам с разными интерфейсами работать сообща. Это работает так:

1. Адаптер имеет интерфейс, который совместим с одним из объектов.
2. Поэтому этот объект может свободно вызывать методы адаптера.
3. Адаптер получает эти вызовы и перенаправляет их второму объекту, но уже в том формате и последовательности, которые понятны второму объекту.

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

<figure><img src="https://refactoringguru.cn/images/patterns/diagrams/adapter/solution-en.png?id=5f4f1b4575236a3853f274b690bd6656" alt="Структура программы после применения адаптера" width="530"><figcaption><p>Программа может работать со сторонней библиотекой через адаптер.</p></figcaption></figure>

Таким образом, в приложении биржевых котировок вы могли бы создать класс `XML_To_JSON_Adapter`, который бы оборачивал объект того или иного класса библиотеки аналитики. Ваш код посылал бы адаптеру запросы в формате XML, а адаптер сначала транслировал входящие данные в формат JSON, а затем передавал бы их методам обёрнутого объекта аналитики.

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

<figure><img src="https://refactoringguru.cn/images/patterns/content/adapter/adapter-comic-1-ru.png?id=85783cd786e264ded1022c1053872ceb" alt="Пример паттерна Адаптер" width="533"><figcaption><p>Содержимое чемоданов до и после поездки за границу.</p></figcaption></figure>

Когда вы в первый раз летите за границу, вас может ждать сюрприз при попытке зарядить ноутбук. Стандарты розеток в разных странах отличаются. Ваша европейская зарядка будет бесполезна в США без специального адаптера, позволяющего подключиться к розетке другого типа.

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

**Адаптер объектов**

Эта реализация использует агрегацию: объект адаптера «оборачивает», то есть содержит ссылку на служебный объект. Такой подход работает во всех языках программирования.

<figure><img src="https://refactoringguru.cn/images/patterns/diagrams/adapter/structure-object-adapter.png?id=33dffbe3aece294162440c7ddd3d5d4f" alt="Структура классов паттерна Адаптер (адаптер объектов)" width="580"><figcaption></figcaption></figure>

1. **Клиент** — это класс, который содержит существующую бизнес-логику программы.
2. **Клиентский интерфейс** описывает протокол, через который клиент может работать с другими классами.
3. **Сервис** — это какой-то полезный класс, обычно сторонний. Клиент не может использовать этот класс напрямую, так как сервис имеет непонятный ему интерфейс.
4. **Адаптер** — это класс, который может одновременно работать и с клиентом, и с сервисом. Он реализует клиентский интерфейс и содержит ссылку на объект сервиса. Адаптер получает вызовы от клиента через методы клиентского интерфейса, а затем переводит их в вызовы методов обёрнутого объекта в правильном формате.
5. Работая с адаптером через интерфейс, клиент не привязывается к конкретному классу адаптера. Благодаря этому, вы можете добавлять в программу новые виды адаптеров, независимо от клиентского кода. Это может пригодиться, если интерфейс сервиса вдруг изменится, например, после выхода новой версии сторонней библиотеки.

**Адаптер классов**

Эта реализация базируется на наследовании: адаптер наследует оба интерфейса одновременно. Такой подход возможен только в языках, поддерживающих множественное наследование, например, C++.

<figure><img src="https://refactoringguru.cn/images/patterns/diagrams/adapter/structure-class-adapter.png?id=e1c60240508146ed3b98ac562cc8e510" alt="Структура классов паттерна Адаптер (адаптер классов)" width="550"><figcaption></figcaption></figure>

1. **Адаптер классов** не нуждается во вложенном объекте, так как он может одновременно наследовать и часть существующего класса, и часть сервиса.

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

&#x20;Когда вы хотите использовать сторонний класс, но его интерфейс не соответствует остальному коду приложения.

&#x20;Адаптер позволяет создать объект-прокладку, который будет превращать вызовы приложения в формат, понятный стороннему классу.

&#x20;Когда вам нужно использовать несколько существующих подклассов, но в них не хватает какой-то общей функциональности, причём расширить суперкласс вы не можете.

&#x20;Вы могли бы создать ещё один уровень подклассов и добавить в них недостающую функциональность. Но при этом придётся дублировать один и тот же код в обеих ветках подклассов.

Более элегантным решением было бы поместить недостающую функциональность в адаптер и приспособить его для работы с суперклассом. Такой адаптер сможет работать со всеми подклассами иерархии. Это решение будет сильно напоминать паттерн [Декоратор](https://refactoringguru.cn/ru/design-patterns/decorator).

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

1. Убедитесь, что у вас есть два класса с несовместимыми интерфейсами:
   * полезный *сервис* — служебный класс, который вы не можете изменять (он либо сторонний, либо от него зависит другой код);
   * один или несколько *клиентов* — существующих классов приложения, несовместимых с сервисом из-за неудобного или несовпадающего интерфейса.
2. Опишите клиентский интерфейс, через который классы приложения смогли бы использовать класс сервиса.
3. Создайте класс адаптера, реализовав этот интерфейс.
4. Поместите в адаптер поле, которое будет хранить ссылку на объект сервиса. Обычно это поле заполняют объектом, переданным в конструктор адаптера. В случае простой адаптации этот объект можно передавать через параметры методов адаптера.
5. Реализуйте все методы клиентского интерфейса в адаптере. Адаптер должен делегировать основную работу сервису.
6. Приложение должно использовать адаптер только через клиентский интерфейс. Это позволит легко изменять и добавлять адаптеры в будущем.

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

* &#x20;Отделяет и скрывает от клиента подробности преобразования различных интерфейсов.
* &#x20;Усложняет код программы из-за введения дополнительных классов.

## Пример реализации

<pre class="language-cpp"><code class="lang-cpp"># include &#x3C;iostream>
# include &#x3C;memory>

using namespace std;

class BaseAdaptee
{
<strong>public:
</strong>        virtual ~BaseAdaptee() = default;

<strong>        virtual void specificRequest() = 0;
</strong>};

class ConAdaptee : public BaseAdaptee
{
public:
        virtual void specificRequest() override { cout &#x3C;&#x3C; "Method ConAdaptee;" &#x3C;&#x3C; endl; }
};

class Adapter
{
public:
        virtual ~Adapter() = default;

        virtual void request() = 0;
};

class ConAdapter : public Adapter
{
private:
        shared_ptr&#x3C;BaseAdaptee>  adaptee;

public:
        ConAdapter(shared_ptr&#x3C;BaseAdaptee> ad) : adaptee(ad) {}

        virtual void request() override;
};

# pragma region Methods
void ConAdapter::request()
{
        cout &#x3C;&#x3C; "Adapter: ";

        if (adaptee)
        {
<strong>                adaptee->specificRequest();
</strong>        }
        else
        {
                cout &#x3C;&#x3C; "Empty!" &#x3C;&#x3C; endl;
        }
}

# pragma endregion

int main()
{
        shared_ptr&#x3C;BaseAdaptee> adaptee = make_shared&#x3C;ConAdaptee>();
        shared_ptr&#x3C;Adapter> adapter = make_shared&#x3C;ConAdapter>(adaptee);

<strong>        adapter->request();
</strong>}

</code></pre>

## Шаблон адаптер

```cpp
# include <iostream>
# include <memory>
# include <vector>

using namespace std;

class Interface
{
public:
    virtual ~Interface() = default;

    virtual void request() = 0;
};

template <typename Type>
class Adapter : public Interface
{
public:
    using PtrMethod_t = void (Type::*)();
    using CallBack_t = pair<shared_ptr<Type>, PtrMethod_t>;

    Adapter(shared_ptr<Type> object, PtrMethod_t method) : callback(object, method) {}

    void request() override { ((*callback.first).*callback.second)(); }

private:
    CallBack_t callback;
};

class AdapteeA
{
public:
    ~AdapteeA() { cout << "Destructor class AdapteeA;" << endl; }

    void specRequestA() { cout << "Method AdapteeA::specRequestA;" << endl; }
};

class AdapteeB
{
public:
    ~AdapteeB() { cout << "Destructor class AdapteeB;" << endl; }

    void specRequestB() { cout << "Method AdapteeB::specRequestB;" << endl; }
};

auto initialize()
{
    using InterPtr = shared_ptr<Interface>;

    vector<InterPtr> vec{
        make_shared<Adapter<AdapteeA>>(make_shared<AdapteeA>(), &AdapteeA::specRequestA),
        make_shared<Adapter<AdapteeB>>(make_shared<AdapteeB>(), &AdapteeB::specRequestB)
    };

    return vec;
}

int main()
{
    auto v = initialize();

    for (const auto& elem : v)
        elem->request();
}

```
