Adapter/Wrapper

Назначение

Adapter обеспечивает взаимодействие двух классов путем преобразования интерфейса одного из них таким образом, чтобы им мог пользоваться другой класс.

  • Нужно использовать класс, который вызывает метод интерфейса, причем необходимо использовать его с классом, который не реализует этот интерфейс;
  • Возможно требуется преобразование аргументов или динамический выбор определенного метода во время выполнения.

Описание

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

Реализация

Реализация шаблона проста, за исключением одного момента: откуда класс адаптер получает экземпляры адаптируемого класса? Есть два подхода:

  1. Передать ссылку через параметр конструктора или метода.
  2. Сделать класс-адаптер внутренним классом Adaptee (если есть возможность изменить его). В этом случае связь Adapter — Adaptee устанавливается автоматически, т.к. внутренний класс принадлежит объекту внешнего класса.

Client
Client
Клиент, взаимодействующий с объектами, которые должны реализовывать требуемый интерфейс.
TargetIF
Reader
Требуемый интерфейс, необходимый для реализации объектами, взаимодействующими с клиентом.
Adapter
InputStreamReader
Класс, реализующий требуемый интерфейс и транслирующий вызовы этого интерфейса в вызовы метода адаптируемого объекта (или его интерфейса).
Adaptee
FileInputStream
Класс, который необходимо использовать для взаимодействия с клиентом, но который не реализует требуемый интерфейс.

Примеры

  • java.util.Arrays#asList()
  • java.io.InputStreamReader

Чем отличается

Decorator Adapter предназначен для изменения интерфейса объекта с сохранением функциональности. Декоратор, наоборот, оставляет интерфейс объекта неизменным, но при этом меняет (добавляет) функциональность.

Proxy Интерфейс, предоставляемый шаблоном Adapter, отличается от интерфейса объекта, а в случае с Proxy – совпадает. То есть объект Proxy имеет тот же интерфейс, заменителем которого он является.

Bridge У шаблона Bridge другое предназначение: он разделяет абстракцию компонента от его реализации, что позволяет изменять абстракцию или реализацию независимо друг от друга. Adapter нужен для использования готового объекта, который просто не подходит по интерфейсу. Bridge можно воспринимать как обобщение Strategy.

Facade Фасад через предоставление простого интерфейса позволяет изолировать клиента от сложных взаимосвязей между множеством объектов, в то время как Adapter приспосабливает один объект для использования через интерфейс, который он не реализует.

Iterator является примером Adapter. Он адаптирует коллекцию к интерфейсу последовательного перебора.

Варианты

  1. Можно использовать два интерфейса – требуемый целевой интерфейс (через который клиент взаимодействует с адаптером) и интерфейс адаптируемого объекта ( через который адаптер взаимодействует с адаптируемым объектом).
  2. Один “объединяющий” адаптер для нескольких адаптируемых объектов (этот случай ближе к паттерну Facade).

Ссылки

https://java-design-patterns.com/patterns/adapter/

https://github.com/iluwatar/java-design-patterns/tree/master/adapter

StackOverflow: How do the Proxy, Decorator, Adapter, and Bridge Patterns differ?

StackOverflow: Examples of GoF Design Patterns in Java’s core libraries

StackOverflow: Use for the adapter pattern in Java

https://refactoring.guru/design-patterns/adapter

Baeldung - The Adapter Pattern in Java