Что это такое
Паттерн Adapter («Адаптер») позволяет классу с неподходящим интерфейсом вписаться в интерфейс, который ожидает ваш код, — как дорожная вилка-переходник между иностранной розеткой и вашим зарядным устройством.
Вы не можете изменить старый класс (он приходит из библиотеки, или от него зависит другой код). Поэтому вы оборачиваете его в небольшой класс, который реализует нужный вам интерфейс и переводит вызовы.
Пример: старый принтер в роли логгера
Нашему коду нужен Logger с методом log(message). Устаревший класс предлагает только writeLine(prefix, text). Адаптер хранит старый объект и переводит один вызов в другой.
Адаптер переводит вызов
run() работает с любым Logger; адаптер делает устаревший принтер одним из них, не трогая его код. Обе выведенные строки начинаются с LOG | — этот префикс добавляет адаптер, а не вызывающий код.
Вывод: программа на TypeScript и захваченный вывод
// Интерфейс, который ожидает наш код.
interface Logger {
log(message: string): void
}
// Старый класс из библиотеки с другим методом — мы не можем его изменить.
class LegacyPrinter {
writeLine(prefix: string, text: string): void {
console.log(prefix + " | " + text)
}
}
// Адаптер делает старый класс подходящим под нужный нам интерфейс.
class PrinterAdapter implements Logger {
private legacy: LegacyPrinter
constructor(legacy: LegacyPrinter) {
this.legacy = legacy
}
log(message: string): void {
this.legacy.writeLine("LOG", message)
}
}
function run(logger: Logger): void {
logger.log("сервис запущен")
logger.log("запрос обработан")
}
run(new PrinterAdapter(new LegacyPrinter()))
Когда это использовать
Используйте, когда рабочий код и нужный интерфейс не совпадают, а изменить ни одну из сторон вы не можете (или не должны) — сторонние библиотеки, устаревший код, сгенерированный код.
Не используйте, когда обе стороны принадлежат вам — тогда просто измените одну из них так, чтобы они совпали. Адаптер между двумя классами, которыми вы управляете, — это обходной путь вместо решения, которое можно просто принять.
Плюсы и минусы
Плюсы: старый и новый код работают вместе без изменений с обеих сторон; перевод живёт в одном небольшом, легко тестируемом классе.
Минусы: ещё один переход, который нужно прочитать; множество адаптеров, наложенных друг на друга, — признак того, что сами интерфейсы нуждаются в переработке.