Паттерн Адаптер
Часть раздела Объектно-ориентированное программирование путешествия по Dart на Coddy — урок 95 из 110.
Паттерн Адаптер (Adapter pattern) позволяет несовместимым интерфейсам работать вместе. Он выступает в качестве моста между двумя классами, которые иначе не могли бы взаимодействовать, оборачивая существующий класс новым интерфейсом, который ожидают клиенты. Это особенно полезно при интеграции сторонних библиотек или устаревшего кода в ваше приложение.
Представьте, что у вас есть система, которая работает с данными JSON, но вам нужно интегрировать компонент, который выдает только XML. Вместо того чтобы изменять какой-либо из классов, вы создаете адаптер, который осуществляет преобразование между ними:
// Существующий интерфейс, который ожидает ваша система
abstract class JsonData {
String getJson();
}
// Устаревший класс с несовместимым интерфейсом
class XmlService {
String getXml() {
return '<data><name>John</name></data>';
}
}
// Адаптер делает XmlService совместимым с JsonData
class XmlToJsonAdapter implements JsonData {
final XmlService _xmlService;
XmlToJsonAdapter(this._xmlService);
@override
String getJson() {
String xml = _xmlService.getXml();
// Преобразование XML в JSON (упрощенно)
return '{"name": "John"}';
}
}
void main() {
var xmlService = XmlService();
var adapter = XmlToJsonAdapter(xmlService);
print(adapter.getJson()); // {"name": "John"}
}XmlToJsonAdapter оборачивает XmlService и реализует интерфейс JsonData. Клиентский код работает с привычным интерфейсом JsonData, не зная о том, что данные изначально поступили из XML-источника. Адаптер выполняет все преобразования внутри себя.
Используйте паттерн Адаптер, когда вы хотите использовать существующий класс, но его интерфейс не соответствует вашим потребностям, или когда вам нужно создать повторно используемый класс, который взаимодействует с классами, имеющими несовместимые интерфейсы.
Задание
ЛегкоДавайте создадим систему адаптера для медиаплеера! Представьте, что у вас есть современная аудиосистема, которая ожидает, что все источники медиа будут предоставлять аудио через стандартный интерфейс, но вам нужно интегрировать старый виниловый проигрыватель, у которого совершенно другой способ воспроизведения звука.
Вы создадите два файла для организации реализации паттерна «Адаптер»:
media_adapter.dart: Этот файл содержит ваш интерфейс и классы адаптера. Определите абстрактный классAudioSource, который ожидает ваша современная система — у него должен быть методString playAudio(), возвращающий аудиовыход в виде строки. Затем создайте классVinylPlayer, представляющий устаревшее оборудование — у него есть полеtrackNameи методString spinRecord(), который возвращает*crackle* Playing vinyl: [trackName] *crackle*. Наконец, создайтеVinylAdapter, который реализуетAudioSourceи оборачиваетVinylPlayer. Метод адаптераplayAudio()должен вызывать метод проигрывателя винилаspinRecord()и возвращать результат, связывая несовместимые интерфейсы.main.dart: Импортируйте файл медиаадаптера и продемонстрируйте, как адаптер заставляет старый виниловый проигрыватель работать с кодом, который ожидает современный интерфейсAudioSource. СоздайтеVinylPlayerс названием трекаBohemian Rhapsody. Затем создайтеVinylAdapter, который оборачивает этот виниловый проигрыватель. Сохраните адаптер в переменной типаAudioSource, чтобы доказать, что он соответствует ожидаемому интерфейсу. ВызовитеplayAudio()у вашего источника звука и выведите результат.
Прелесть этого паттерна в том, что ваш основной код работает со стандартным интерфейсом AudioSource — ему не нужно знать, что за кулисами всю реальную работу выполняет старый виниловый проигрыватель!
Ожидаемый вывод:
*crackle* Playing vinyl: Bohemian Rhapsody *crackle*Шпаргалка
Паттерн Адаптер позволяет несовместимым интерфейсам работать вместе, выступая в качестве моста между двумя классами, которые иначе не могли бы взаимодействовать.
Адаптер оборачивает существующий класс новым интерфейсом, который ожидают клиенты, что делает его полезным при интеграции сторонних библиотек или устаревшего кода.
Базовая структура
// Целевой интерфейс, который ожидают клиенты
abstract class JsonData {
String getJson();
}
// Существующий класс с несовместимым интерфейсом
class XmlService {
String getXml() {
return '<data><name>John</name></data>';
}
}
// Адаптер делает XmlService совместимым с JsonData
class XmlToJsonAdapter implements JsonData {
final XmlService _xmlService;
XmlToJsonAdapter(this._xmlService);
@override
String getJson() {
String xml = _xmlService.getXml();
// Преобразование XML в JSON
return '{"name": "John"}';
}
}Использование
void main() {
var xmlService = XmlService();
var adapter = XmlToJsonAdapter(xmlService);
print(adapter.getJson()); // {"name": "John"}
}Адаптер реализует целевой интерфейс, оборачивая при этом несовместимый класс. Клиентский код работает со знакомым интерфейсом, не зная о внутренней реализации.
Когда использовать: Когда вам нужно использовать существующий класс, но его интерфейс не соответствует вашим потребностям, или при создании повторно используемого класса, который должен взаимодействовать с классами, имеющими несовместимые интерфейсы.
Попробуйте сами
import 'media_adapter.dart';
void main() {
// TODO: Создайте VinylPlayer с названием трека "Bohemian Rhapsody"
// TODO: Создайте VinylAdapter, который оборачивает vinyl player
// TODO: Сохраните адаптер в переменную типа AudioSource
// TODO: Вызовите playAudio() у вашего аудиоисточника и выведите результат
}
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Объектно-ориентированное программирование
1Основы ООП
Внешние файлыБиблиотеки и импортВведение в ООПКлассы и объектыКлючевое слово thisМетодыПеременные экземпляраОсновы конструкторовИтоги — Простой калькулятор4Null-безопасность
Введение в Null-безопасностьNullable и Non-Nullable типыОператоры ? и !Ключевое слово Late и Null-безопасностьNull-aware операторыNull-безопасность в классахИтоги — Система профилей пользователей7Абстрактные классы и интерфейсы
Абстрактные классыАбстрактные методыИнтерфейсы в DartНеявные интерфейсыРеализация против наследованияМножественные интерфейсыИтоги — Калькулятор фигур10Коллекции и обобщения
Обзор List, Set, MapТипобезопасные коллекцииОбобщенные классыОбобщенные методыОграничения обобщенийIterable и IteratorИтоги: Обобщенное хранилище13Продвинутые концепции ООП
Композиция против наследованияМетоды расширенияВызываемые классыЗапечатанные классы (Dart 3)Записи (Dart 3)Паттерны и сопоставление (3.0)Перечисления с методами16Проект: Управление библиотекой
Обзор проектаКлассы Book и User2Конструкторы в Dart
Конструктор по умолчаниюИменованные конструкторыСписки инициализацииКонстантные конструкторыФабричные конструкторыПеренаправляющие конструкторыИтоги — Shape Builder5Инкапсуляция
Публичные и приватные членыСоглашение о префиксе _Приватность на уровне библиотекГеттеры и сеттеры: подробный разборСкрытие данныхИтоги — Записи о студентах8Mixins
Введение в MixinsСоздание MixinsИспользование нескольких MixinsКлючевое слово on в MixinsMixin и наследованиеMixin и интерфейсИтоги: Система животных11Специальные методы
Переопределение toString()Переопределение hashCode и ==Интерфейс ComparableМетод call()Переопределение noSuchMethodПовторение — Пользовательская коллекция14Паттерны проектирования. Часть 1
Введение в паттерны проектированияПаттерн SingletonПаттерн FactoryПаттерн ObserverПаттерн Strategy3Свойства классов
Члены экземпляра и статические членыПоля Final и ConstПеременные LateСтатические методы и поляГеттеры и сеттерыИтоги — Менеджер банковского счета6Наследование
Основы наследованияКлючевое слово superПереопределение методовАннотация @overrideКлючевое слово final для классовКонструкторы и наследованиеИтоги: Иерархия сотрудников9Полиморфизм
Основы полиморфизмаПолиморфизм через интерфейсыПроверка типов во время выполненияОператоры is и asКлючевое слово covariantИтоги — Обработчик платежей12Асинхронное ООП
Futures и async/awaitОсновы StreamsКонтроллеры StreamАсинхронные конструкторыАсинхронность в методах классовИтоги — Data Fetcher15Паттерны проектирования. Часть 2
Паттерн КомандаПаттерн АдаптерПаттерн ДекораторПаттерн Шаблонный методПаттерн СостояниеПаттерн КомпоновщикПаттерн Репозиторий