Ответ
Я использовал паттерн Адаптер (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;
}
В общем, применяю я эту штуку, когда нужно запихнуть в новую систему какой-нибудь проверенный временем, но уёбищный на вид кусок кода, а лезть в его кишки и переписывать — себе дороже. Просто делаешь ему переводчика, и пусть работает, как проклятый. Ёперный театр, а не паттерн.