Зачем нужен?
Паттерн Adapter применяется, когда у вас имеется готовый компонент, реализующий требуемую функциональность, но его API несовместимо с философией/архитектурой остальной системы.
Определение
- преобразует интерфейс класса к другому интерфейсу, на который рассчитан клиент. Адаптер обеспечивает совместную работу классов, невозможную в обычных условиях из-за несовместимости интерфейсов.
- обертывает(упаковывает) существующий класс в новый интерфейс
- приводит в соответствие старый компонент к новой системе
Реализация
Переиспользование кода очень часто является болезненным и труднодостижимым процессом. Одна из проблем - проектирование чего-то нового, при этом используя что-то старое. Но всегда есть какое-то несоответствие между новым и старым. Тут-то и пригодится связующий компонент, адаптер, который приведет в соответствие старый компонент к новой системе.
Клиенты вызывают методы у объекта Адаптера, который перенаправляет вызовы к унаследованному компоненту. Адаптер может быть реализован с помощью наследования(адаптер классов) или с помощью композиции(агрегации). Паттерн Адаптер и адаптер в реальной мире - по сути одно и тоже.
Реализация похожа на паттерн фасад, но задачи у них разные.
- Идентифицируйте клиента(компонент к которому надо приспосабливаться) и адаптируемый объект.
- Определите интерфейс, который требует клиент
- Спроектируйте класс-адаптер: он должен содержать адаптируемый объект(композиция) и делегировать интерфейс клиента адаптируемому объекту
Пример
Допустим у нас есть утки. Но уток мало и мы хотим в качестве уток использовать еще и индейку. Индейка не может крякать, но может гоготать. Также она не может далеко летать.
Идентифицируем клиента и его протокол(интерфейс):
protocol Duck {
func quack()
func fly()
}
// клиент
class MallardDuck: Duck {
func quack() {
print("Quack-quack")
}
func fly() {
print("I'm flying")
}
}
Класс, который мы хотим адаптировать:
protocol Turkey {
func gobble()
func fly()
}
// адаптируемый интерфейс
class WildTurkey: Turkey {
func gobble() {
print("Gobble-gobble")
}
func fly() {
print("I'm flying but only on a short distances :(")
}
}
Спроектируем адаптер
// адаптер
class TurkeyAdapter: Duck {
private let turkey: Turkey
init(turkey: Turkey) {
self.turkey = turkey
}
func quack() {
turkey.gobble()
}
func fly() {
for _ in 0..<5 {
turkey.fly()
}
}
}
Теперь мы можем спокойно использовать индейку и даже не подозревать, что это индейка.