Что такое паттерн проектирования Адаптер (Adapter)?

Ответ

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

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

Участники:

  1. Целевой интерфейс (Target): Интерфейс, который ожидает клиент.
  2. Адаптируемый класс (Adaptee): Существующий класс с несовместимым интерфейсом.
  3. Адаптер (Adapter): Класс, который реализует целевой интерфейс и оборачивает экземпляр адаптируемого класса, переводя вызовы.

Реализация (объектный адаптер, использует композицию):

// 1. Целевой интерфейс, ожидаемый клиентским кодом (новый стандарт)
interface USB_C {
    void connectWithUsbC();
}

// 2. Адаптируемый класс со старым интерфейсом
class MicroUSBCharger {
    public void plugInMicroUSB() {
        System.out.println("Charging via MicroUSB...");
    }
}

// 3. Адаптер
class MicroUSBToUSBCAdapter implements USB_C {
    private MicroUSBCharger microUSBCharger; // Композиция

    public MicroUSBToUSBCAdapter(MicroUSBCharger charger) {
        this.microUSBCharger = charger;
    }

    @Override
    public void connectWithUsbC() {
        // Адаптер преобразует вызов нового метода в старый
        System.out.print("Adapter is converting USB-C to MicroUSB. ");
        microUSBCharger.plugInMicroUSB();
    }
}

// 4. Клиентский код
public class Client {
    public static void main(String[] args) {
        USB_C modernPort = new MicroUSBToUSBCAdapter(new MicroUSBCharger());
        modernPort.connectWithUsbC(); // Работает со старым зарядным устройством!
    }
}
// Вывод: Adapter is converting USB-C to MicroUSB. Charging via MicroUSB...

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

  • Интеграция старого легаси-кода или сторонних библиотек.
  • Необходимость использовать несколько классов с разными интерфейсами единообразно.
  • Вы хотите изолировать основной код от деталей конкретных реализаций.

Отличие от Декоратора: Адаптер меняет интерфейс объекта, а Декоратор расширяет его функциональность, не меняя интерфейса.

Ответ 18+ 🔞

Давай я тебе на пальцах объясню, что за зверь такой — этот Адаптер. Представь, у тебя есть новый, охуенный телефон с разъёмом USB-C, а зарядка — старая, с этим дурацким MicroUSB. И ты такой: «Ну ёпта, как теперь заряжать?». А потом находишь в ящике эту маленькую хуйню — переходничок. Вот это и есть адаптер, блядь! Он берёт один интерфейс (твой новый порт) и превращает его в другой (старый разъём), чтобы всё заработало. Гениально и просто, как тапок.

Кто тут кто, разберём по косточкам:

  1. Целевой интерфейс (Target): Это то, чего ты хочешь. Твой новый телефон орет: «Давай сюда USB-C, иначе нихуя не будет!».
  2. Адаптируемый класс (Adaptee): Это твоя старая зарядка, упоротая, которая мычит: «Я умею только в MicroUSB пихаться, иди нахуй».
  3. Адаптер (Adapter): Сам переходник, хитрая жопа. С одной стороны он делает вид, что он USB-C (целевой интерфейс), а с другой — внутри у него торчит эта старая зарядка, и он шепчет ей: «Спокойно, я всё уладю».

Смотри, как это в коде выглядит, тут всё честно:

// 1. Это твой новый, строгий порядок. Хочу USB-C и всё тут!
interface USB_C {
    void connectWithUsbC();
}

// 2. А это дед, который в пещере жил. Умеет только одно.
class MicroUSBCharger {
    public void plugInMicroUSB() {
        System.out.println("Charging via MicroUSB...");
    }
}

// 3. А вот и наш спаситель, переводчик с буржуйского на деревенский!
class MicroUSBToUSBCAdapter implements USB_C {
    private MicroUSBCharger microUSBCharger; // Прячет старика внутри

    public MicroUSBToUSBCAdapter(MicroUSBCharger charger) {
        this.microUSBCharger = charger;
    }

    @Override
    public void connectWithUsbC() {
        // Магия происходит здесь! Говорит на новом языке, а делает по-старому.
        System.out.print("Adapter is converting USB-C to MicroUSB. ");
        microUSBCharger.plugInMicroUSB(); // А тут старикан своё дело делает
    }
}

// 4. Ты, довольный пользователь
public class Client {
    public static void main(String[] args) {
        // Берёшь старую зарядку, суёшь в адаптер — и вуаля, она теперь как новая!
        USB_C modernPort = new MicroUSBToUSBCAdapter(new MicroUSBCharger());
        modernPort.connectWithUsbC(); // И работает, ёба!
    }
}
// На экране: Adapter is converting USB-C to MicroUSB. Charging via MicroUSB...

Когда эту штуку впиндюривать в проект?

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

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