Когда стоит использовать паттерн Adapter?

Ответ

Я использовал паттерн Адаптер (Adapter) для интеграции сторонних библиотек или унаследованного кода, интерфейс которого не совместим с интерфейсом, ожидаемым в моей системе. Он позволяет работать со старым кодом, не изменяя его.

Типичный пример из моего опыта: нужно было использовать старую библиотеку для работы с графикой (например, с устаревшим API рисования), в то время как основное приложение работало с современным интерфейсом ModernRenderer.

// Интерфейс, который ожидает наша система (Цель)
class ModernRenderer {
public:
    virtual void drawShape(float x, float y, float width, float height) = 0;
};

// Устаревший класс, который нужно адаптировать (Адаптируемый)
class LegacyGraphics {
public:
    void render(int x1, int y1, int x2, int y2) {
        // Старый код рисования прямоугольника
        std::cout << "Legacy draw: (" << x1 << "," << y1 << ") to (" << x2 << "," << y2 << ")n";
    }
};

// Адаптер, наследует цель и содержит адаптируемый объект
class GraphicsAdapter : public ModernRenderer {
    LegacyGraphics* legacyGraphics;
public:
    GraphicsAdapter(LegacyGraphics* lg) : legacyGraphics(lg) {}

    void drawShape(float x, float y, float w, float h) override {
        // Преобразуем новый интерфейс в старый
        legacyGraphics->render(static_cast<int>(x), static_cast<int>(y),
                               static_cast<int>(x + w), static_cast<int>(y + h));
    }
};

// Использование в клиентском коде
int main() {
    LegacyGraphics oldLib;
    GraphicsAdapter adapter(&oldLib);

    // Теперь можно использовать старую библиотеку через новый интерфейс
    ModernRenderer* renderer = &adapter;
    renderer->drawShape(10.5f, 20.5f, 100.0f, 50.0f);
    return 0;
}

Я применяю Adapter, когда нужно повторно использовать проверенный, но устаревший код, и когда изменение его исходников невозможно или нецелесообразно.

Ответ 18+ 🔞

А, паттерн Адаптер! Ну это ж классика, ебать мои старые костыли. Представь себе ситуацию: у тебя есть какая-нибудь древняя библиотека, которую писал ещё какой-нибудь полупидор в 2002-м году. Код там рабочий, железобетонный, но интерфейс у него — пизда рулю. А твоя новая, красивая система хочет общаться с ним на современном языке. Вот тут-то этот самый Адаптер и выручает, как хитрая жопа.

Вот реальный пиздец из моей практики: пришлось впихнуть в проект старую графическую библиотеку, которая умела рисовать только по старым, целочисленным координатам. А вся моя архитектура уже заточена под новый, красивый интерфейс ModernRenderer с float'ами. Доверия к этому старому коду было, конечно, ебать ноль, но переписывать его — это ж овердохуища работы.

// Вот что хочет наша новая система (Цель, так сказать)
class ModernRenderer {
public:
    virtual void drawShape(float x, float y, float width, float height) = 0;
};

// А вот этот хуй с горы, который достался в наследство (Адаптируемый объект)
class LegacyGraphics {
public:
    void render(int x1, int y1, int x2, int y2) {
        // Древний код, который просто работает, хоть и пахнет нафталином
        std::cout << "Legacy draw: (" << x1 << "," << y1 << ") to (" << x2 << "," << y2 << ")n";
    }
};

// Сам Адаптер. Наследуемся от нового, а внутри держим старый обрубок.
class GraphicsAdapter : public ModernRenderer {
    LegacyGraphics* legacyGraphics;
public:
    GraphicsAdapter(LegacyGraphics* lg) : legacyGraphics(lg) {}

    void drawShape(float x, float y, float w, float h) override {
        // Вся магия здесь: превращаем красивые float'ы в те самые int'ы, которые жрёт старина
        legacyGraphics->render(static_cast<int>(x), static_cast<int>(y),
                               static_cast<int>(x + w), static_cast<int>(y + h));
    }
};

// А вот как этим всем пользоваться
int main() {
    LegacyGraphics oldLib; // Наш динозавр
    GraphicsAdapter adapter(&oldLib); // Надеваем на него фрак и цилиндр

    // И вуаля! Теперь он умеет говорить на языке джентльменов
    ModernRenderer* renderer = &adapter;
    renderer->drawShape(10.5f, 20.5f, 100.0f, 50.0f); // И ни один float не пострадал
    return 0;
}

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