Adapter (Адаптер)¶
Категория: структурный паттерн.
Проблема¶
Есть готовый класс с полезной функциональностью (свой, из другой части системы или из сторонней библиотеки), но его интерфейс не совпадает с тем, что ожидает остальной код. Менять сам класс нельзя или нежелательно (сторонний код, много мест использования, риск сломать существующую функциональность).
Решение¶
Создаётся класс-адаптер, который:
1. Реализует интерфейс, ожидаемый клиентом (Target).
2. Хранит ссылку на оборачиваемый объект (Adaptee) с несовместимым интерфейсом.
3. В реализации методов Target вызывает соответствующие методы Adaptee, при необходимости преобразуя данные и параметры.
Клиентский код продолжает работать с абстракцией Target и ничего не знает о существовании Adaptee - вся "склейка" спрятана внутри адаптера.
Структура¶
Target- интерфейс, которым пользуется клиент.Adaptee- существующий класс с несовместимым интерфейсом.Adapter- реализуетTarget, делегируя вызовыAdaptee.Client- работает сTarget, не зная проAdaptee.
Object Adapter vs Class Adapter¶
В классическом GoF (C++) выделяют два варианта:
- Object Adapter - адаптер хранит ссылку на Adaptee и делегирует вызовы (композиция). Это единственный практичный вариант в C#, так как язык не поддерживает множественное наследование классов.
- Class Adapter - адаптер наследуется одновременно от Target и Adaptee. В C# недостижимо напрямую (можно наследоваться только от одного класса), поэтому не рассматривается как реальный вариант на этой платформе.
Когда применять¶
- Нужно использовать существующий класс, но его интерфейс не подходит под остальной код (особенно при интеграции сторонних библиотек).
- Нужно создать переиспользуемый класс, который должен работать с разными, заранее непредсказуемыми классами, не обязательно связанными общим интерфейсом.
Плюсы¶
- Позволяет переиспользовать существующий код без его изменения.
- Отделяет логику преобразования интерфейса от бизнес-логики клиента (принцип единственной обязанности).
Минусы¶
- Добавляет ещё один слой косвенности - лишний класс и вызов на каждое обращение.
- Если несовместимых классов много и они разнородны, адаптеров становится много, и это усложняет навигацию по коду.
Отличие от Facade¶
Adapter меняет интерфейс существующего объекта, чтобы он соответствовал уже существующему, ожидаемому клиентом интерфейсу - обычно оборачивает один объект. Facade создаёт новый, более простой интерфейс к нескольким объектам подсистемы, не обязательно подгоняясь под уже существующий контракт.
Пример в .NET Framework / BCL¶
- Адаптеры LINQ to Objects/LINQ to SQL, приводящие разные источники данных к единому
IQueryable/IEnumerable. DataAdapterв ADO.NET (SqlDataAdapterи т.д.) - явно называется адаптером, связываетDataSetс конкретной СУБД.- Обёртки над
Streamпри чтении данных из разных источников (файл, сеть, память) под единый интерфейсStream.