Шаблон Адаптер предназначен для приведения интерфейса объекта к требуемому виду.
Данный шаблон применяется если:
- существующий объект, называемый адаптируемым, предоставляет необходимые функции, но не поддерживает нужного интерфейса;
- (или) неизвестно заранее, с каким интерфейсами придется работать адаптируемому объекту;
- (или) формат входных или выходных данных метода не совпадает с требуемым.
Главная задача Адаптера – реализация требуемого интерфейса и трансляция его вызовов адаптируемому объекту. Подобную ситуацию можно встретить при использовании сторонних библиотек. Они далеко не всегда предоставляют интерфейсы, которые необходимы в для связи с другими объектами. При этом изменить код или добавить поддержку интерфейса не предоставляется возможным.
Иллюстрацией другого варианта использования может служить подключение модулей расширения. Предположим, что есть готовый компонент выполняющий нужные функции, но ничего не знающий о нужном для использования интерфейсе. Адаптер, в этом случае, будет играть роль переходника для подключения к интерфейсу системы расширений. При этом подключение новых компонентов может изменять функциональность приложения.
Еще один пример использования шаблона – адаптация поведения. Такой подход используется, например, в .NET при работе с COM объектами: полученные от них коды ошибок из значений типа HRESULT преобразуется в выбросы исключений типа COMExceptions.
Таким образом, цель Адаптера – предоставить возможность повторного использования существующего кода, независимо от отличий в интерфейсе или поведении. Кроме того, меняя адаптируемые объекты возможно влиять на функции, выполняемые в программе.
Различают четыре роли, отводимые участвующим в работе шаблона объектам:
- Адаптируемый объект (Adaptee);
- Цель (Target), определяющая требуемый интерфейс;
- Адаптер (Adapter);
- Клиент (Client), который умеет работать с только объектами, реализующими интерфейс цели.
Стоит принять во внимание следующие моменты:
- Адаптер не обязательно должен содержать только вызовы адаптируемого объекта. Он может заниматься обработкой входных и выходных данных, приводя их к нужному формату.
- В рамках шаблона можно использовать несколько адаптируемых объектов для реализации заданного интерфейса.
- Реализация шаблона может самостоятельно дополнить необходимую функциональность, если такой нет у адаптируемого объекта.
- Адаптер может работать не только с заданным адаптируемым объектом, но и его наследниками.
- Возможно замещение части методов адаптируемого объекта. В этом случае от него необходимо создать подкласс, в котором произвести нужные замещения. И уже результат использовать в Адаптере.
- Реализация адаптера может быть двухсторонней. В этом случае адаптируемый объект реализует промежуточную логику для связи двух целей, каждая из которых требует наличие своего интерфейса.
- Можно использовать Отложенную инициализацию для создания экземпляра адаптируемого объекта.
Реализация шаблона в общем виде
По схеме, используемой для работы с адаптируемым объектом, выделяют два варианта:
- Адаптер объекта – использует композицию, т.е. содержит экземпляр адаптируемого объекта.
- Адаптер класса – использует наследование от адаптируемого объекта для получения его функциональности.
Приоритетным является первый вариант, т.к. он обеспечивает меньшую связанность с адаптируемым объектом. В этом случае может даже осуществляться преобразование одного интерфейса в другой без привязки к конкретной реализации.
Но встречаются ситуации, когда требуется применение адаптера класса. Например, необходимость доступа к protected методам. В другом случае может потребоваться использовать Адаптер и вместо адаптируемого объекта.
Реализация Адаптера объекта
- создаем класс Adapter, который будет реализовывать требуемый интерфейс ITarget или являться наследником от класса Target с нужным интерфейсом;
- в его закрытом поле _adaptee размещаем экземпляр адаптируемого объекта;
- реализуем интерфейс ITarget, в методах которого вызываем нужные методы адаптируемого объекта;
- клиент использует экземпляр класса Adapter и получает требуемую функциональность.
Реализация Адаптера класса
- создаем класс Adapter, который будет реализовывать требуемый интерфейс ITarget;шаблон позволяет использовать наследование от класса Target, но в C# этот вариант не может быть реализован. Причина – запрет на множественное наследование.
- добавлением классу Adapter наследование от адаптируемого класса;
- реализуем интерфейс ITarget, в методах которого вызываем нужные методы адаптируемого объекта;
- клиент использует экземпляр класса Adapter и получает требуемую функциональность.
Примеры реализации
Пример реализации Адаптер на уровне объекта
В примере рассмотрим адаптацию едениц измерения в фунтах к киллограммам
Создадим интерфейс весов IScales
Реализуем интерфейс IScales в классе RussianScale
ТСоздадим класс BritishScale
Для согласованности работы BritishScale к измерению в кг создадим класс адаптер.
Посмотрим применение
Как видим с помощью адаптера фунты адаптировались к кг.
Пример реализации Адаптер на уровне класса.
Рассмотрим так же на примере работы весов.
Так же реализуием интерфейс в классе RussianScales
Для согласованности работы BritishScale к измерению в кг создадим класс адаптер.
Посмотрим применение