В чём суть шаблона проектирования Adapter (Адаптер)?

Ответ

Adapter — это структурный паттерн проектирования, который позволяет объектам с несовместимыми интерфейсами работать вместе. Он выступает в роли "переводчика" или "прослойки" между клиентом и адаптируемым сервисом.

Аналогия: Электрический адаптер, позволяющий вилке одного стандарта работать в розетке другого.

Реализация в Java:

// 1. Целевой интерфейс, который ожидает клиент (например, розетка USB-C)
interface UsbCCharger {
    void chargeWithUsbC();
}

// 2. Адаптируемый класс со старым интерфейсом (например, зарядка MicroUSB)
class MicroUsbCharger {
    public void chargeWithMicroUsb() {
        System.out.println("Charging with MicroUSB...");
    }
}

// 3. Адаптер, реализующий целевой интерфейс и содержащий ссылку на адаптируемый объект
class MicroUsbToUsbCAdapter implements UsbCCharger {
    private MicroUsbCharger microUsbCharger;

    public MicroUsbToUsbCAdapter(MicroUsbCharger microUsbCharger) {
        this.microUsbCharger = microUsbCharger;
    }

    @Override
    public void chargeWithUsbC() {
        // Адаптер преобразует вызов нового метода в вызов старого
        microUsbCharger.chargeWithMicroUsb();
        System.out.println("  (via Adapter)");
    }
}

// 4. Клиентский код, который работает только с целевым интерфейсом
public class Client {
    public static void main(String[] args) {
        MicroUsbCharger oldCharger = new MicroUsbCharger();
        // Используем адаптер для подключения старой зарядки к новому устройству
        UsbCCharger adapter = new MicroUsbToUsbCAdapter(oldCharger);
        adapter.chargeWithUsbC(); // Вывод: Charging with MicroUSB... (via Adapter)
    }
}

Когда использовать:

  • Интеграция старого (legacy) кода в новую систему.
  • Использование сторонних библиотек с неподходящим интерфейсом.
  • Создание универсального интерфейса для работы с несколькими классами, имеющими разную функциональность.

Плюсы:

  • Отделяет и скрывает код преобразования интерфейсов от бизнес-логики.
  • Повышает переиспользуемость кода.

Минусы:

  • Усложняет систему за счет введения дополнительных классов.

Ответ 18+ 🔞

Слушай, а вот есть у тебя, допустим, зарядка от старого телефона — MicroUSB, а новый смартфон требует USB-C. И что, выкидывать старую? Да хуй там! Берёшь переходничек, воткни его — и работает. Этот переходничек в мире кода и есть Adapter, ёпта.

Этот паттерн — как раз такая прослойка-переводчик между двумя объектами, которые друг друга нахуй послать готовы из-за разных интерфейсов. Клиент хочет одно, а сервис умеет только другое. Адаптер берёт и говорит: «Да похуй, я переведу».

Как это выглядит в коде, на примере этих ебучих зарядок:

// 1. Это то, что хочет твой новый телефон (USB-C розетка)
interface UsbCCharger {
    void chargeWithUsbC();
}

// 2. А это твоя старая зарядка, которая умеет только так
class MicroUsbCharger {
    public void chargeWithMicroUsb() {
        System.out.println("Charging with MicroUSB...");
    }
}

// 3. А вот и наш герой — переходник-адаптер!
class MicroUsbToUsbCAdapter implements UsbCCharger {
    private MicroUsbCharger microUsbCharger; // держит ссылку на старую хрень

    public MicroUsbToUsbCAdapter(MicroUsbCharger microUsbCharger) {
        this.microUsbCharger = microUsbCharger;
    }

    @Override
    public void chargeWithUsbC() {
        // Магия здесь: он принимает вызов нового метода...
        microUsbCharger.chargeWithMicroUsb(); // ...и внутри дергает старый
        System.out.println("  (via Adapter)");
    }
}

// 4. Клиент (твой телефон) думает, что работает с USB-C, а по факту...
public class Client {
    public static void main(String[] args) {
        MicroUsbCharger oldCharger = new MicroUsbCharger(); // старая зарядка
        // Обманываем систему адаптером
        UsbCCharger adapter = new MicroUsbToUsbCAdapter(oldCharger);
        adapter.chargeWithUsbC(); // Вывод: Charging with MicroUSB... (via Adapter)
    }
}

Когда эту хуйню применять?

  • Когда нужно впихнуть старый легаси-код в новую систему, а переписывать его — овердохуища работы.
  • Когда используешь стороннюю библиотеку, у которой интерфейс кривой как рога у козла.
  • Когда хочешь сделать универсальную заглушку для кучи классов с разными методами.

Плюсы:

  • Разделяет ответственность: весь этот пиздец с преобразованием интерфейсов сидит в одном месте, а не размазан по всей логике.
  • Повышает переиспользуемость: старый код можно таскать за собой, как чемодан без ручки, и везде подключать через адаптер.

Минусы:

  • Усложняет архитектуру: появляются эти ебучие классы-прослойки, которых с каждым днём всё больше. Иногда проще нахуй послать и переписать.