Что это такое
Паттерн Adapter заставляет тип с неподходящим интерфейсом соответствовать тому интерфейсу, который ожидает ваш код, — как дорожный переходник между чужой розеткой и вашей зарядкой.
Вы не можете изменить старый тип (он приходит из библиотеки или от него зависит другой код). Поэтому вы оборачиваете его в небольшой тип, который реализует нужный вам интерфейс и переводит вызовы.
Пример: старый принтер в роли логгера
Нашему коду нужен Logger с методом Log(message). Устаревший тип предлагает только WriteLine(prefix, text). Адаптер хранит старый тип и переводит один вызов в другой.
Адаптер переводит вызов
run() работает с любым Logger; адаптер делает устаревший принтер одним из них, не трогая его код.
Вывод: программа на Go и захваченный вывод
package main
import "fmt"
// Интерфейс, который ожидает наш код.
type Logger interface {
Log(message string)
}
// Старый тип из библиотеки с другим методом — изменить его мы не можем.
type LegacyPrinter struct{}
func (LegacyPrinter) WriteLine(prefix, text string) {
fmt.Println(prefix + " | " + text)
}
// Адаптер делает старый тип подходящим для нового интерфейса.
type PrinterAdapter struct {
legacy LegacyPrinter
}
func (a PrinterAdapter) Log(message string) {
a.legacy.WriteLine("LOG", message)
}
func run(l Logger) {
l.Log("сервис запущен")
l.Log("запрос обработан")
}
func main() {
run(PrinterAdapter{legacy: LegacyPrinter{}})
}
Когда его использовать
Используйте, когда работающий код и нужный интерфейс не совпадают, а изменить любую из сторон вы не можете (или не должны), — сторонние библиотеки, устаревший код, сгенерированный код.
Не используйте, когда обе стороны принадлежат вам: тогда просто измените одну из них, чтобы они совпали. Адаптер между двумя типами, которыми вы управляете, — это обходной путь вместо решения, которое можно было бы просто принять.
Плюсы и минусы
Плюсы: старый и новый код работают вместе без изменений с обеих сторон; перевод вызовов живёт в одном небольшом и легко тестируемом типе.
Минусы: при чтении кода появляется ещё один переход; множество адаптеров, наложенных друг на друга, — признак того, что переработки требуют сами интерфейсы.