Паттерны проектирования
14. Паттерн «Адаптер» (Adapter)
Адаптер является одним из тех паттернов проектирования, которые мы используем, почти не задумываясь. Диаграмма классов этого паттерна настолько общая, что практически любую композицию объектов можно считать примером использования адаптеров.
Назначение: преобразует интерфейс одного класса в интерфейс другого, который ожидают клиенты. Адаптер делает возможной совместную работу классов с несовместимыми интерфейсами.
Причины использования:
Далеко не все системы обладают прекрасным дизайном. Даже если модуль или приложение были хорошо продуманы изначально, внесение изменений разными разработчиками в течение длительного времени может привести к неприятным последствиям. Одно из таких последствий — рассогласованность реализации однотипных задач. Адаптер — это клей, который связывает воедино два мира путем подгонки текущих классов к требуемому интерфейсу.
Классическая диаграмма приведена на рисунке ниже:
-
Target — целевой интерфейс, к которому нужно преобразовать интерфейс существующих классов;-
Adaptee — существующий класс, чей интерфейс нужно преобразовать;-
Adapter — класс-адаптер, который преобразует интерфейс адаптируемого класса к целевому;-
Client — клиенты нового интерфейса, которые работают с адаптированными классами полиморфным образом.Варианты применения:
- Повторное использование чужого кода. В некоторых случаях у нас уже есть код, который решает нужную задачу, но его интерфейс не подходит для текущего приложения. Вместо изменения кода библиотеки можно создать слой адаптеров.
- Адаптивный рефакторинг. Адаптеры позволяют плавно изменять существующую функциональность путем выделения нового «правильного» интерфейса, но с использованием старой проверенной функциональности.
Адаптивный рефакторинг
В некоторых случаях адаптеры могут упростить эволюцию дизайна и рефакторинг. Предположим, что у нас есть иерархия классов, которую мы хотим модифицировать. Можно переписать все классы иерархии, но можно пойти более итеративным путём:
1. Проектируем новый интерфейс с набором нужных методов.
2. Создаём первую реализацию (адаптер), которая реализует интерфейс, но делегирует все операции старой реализации.
3. Переводим клиентов иерархии на использование нового интерфейса и проверяем, что клиенты работают нормально.
4. Создаем полноценную новую реализацию.
5. Удаляем адаптер и старую иерархию за ненадобностью.
Преимущество такого подхода в том, что изменения происходят постепенно, а не одним большим скачком.
Источник: Тепляков С. "Паттерны проектирования на платформе .NET." — СПб.: Питер, 2015. Глава 12.