Паттерн Адаптер
Часть раздела Объектно-ориентированное программирование путешествия по C# на Coddy — урок 57 из 70.
Паттерн Адаптер — это структурный паттерн, который позволяет несовместимым интерфейсам работать вместе. Он действует как мост между двумя классами, преобразуя интерфейс одного класса в интерфейс, который ожидают клиенты. Представьте его как адаптер питания, который позволяет подключить европейское устройство к американской розетке.
Этот паттерн особенно полезен, когда вы хотите использовать существующий класс, но его интерфейс не соответствует потребностям вашего кода. Вместо того чтобы изменять исходный класс (который вы, возможно, не контролируете), вы создаете адаптер, который оборачивает его:
// Существующий класс с несовместимым интерфейсом
public class OldPrinter
{
public void PrintMessage(string msg) => Console.WriteLine($"Old: {msg}");
}
// Интерфейс, который ожидает ваш код
public interface IPrinter
{
void Print(string text);
}
// Адаптер устраняет разрыв
public class PrinterAdapter : IPrinter
{
private OldPrinter _oldPrinter;
public PrinterAdapter(OldPrinter oldPrinter)
{
_oldPrinter = oldPrinter;
}
public void Print(string text)
{
_oldPrinter.PrintMessage(text); // Делегирует вызов старому интерфейсу
}
}Теперь ваш код может работать со старым принтером через ожидаемый интерфейс:
OldPrinter legacy = new OldPrinter();
IPrinter printer = new PrinterAdapter(legacy);
printer.Print("Hello"); // Старый: HelloПаттерн Адаптер (Adapter) неоценим при интеграции сторонних библиотек или устаревшего кода в ваше приложение. Клиентский код работает с интерфейсом IPrinter, совершенно не подозревая о том, что за кулисами реальную работу выполняет старый, несовместимый класс.
Задание
ЛегкоДавайте создадим систему медиаплеера, используя паттерн Адаптер (Adapter). У вас есть существующая аудиобиблиотека, которая воспроизводит MP3-файлы, но вашему приложению требуется унифицированный интерфейс, способный обрабатывать различные типы медиа. Вы создадите адаптер, чтобы преодолеть этот разрыв, позволяя вашему современному медиаплееру беспрепятственно работать с устаревшей аудиобиблиотекой.
Вы организуете свой код в трех файлах:
LegacyAudio.cs: Представляет собой существующую библиотеку, которую вы не можете изменять. Создайте классLegacyAudioPlayerв пространстве именMediaSystemс методомPlayMp3(string filename), который возвращает строку"Playing MP3: {filename}". Это ваш «адаптируемый объект» (adaptee) — класс с несовместимым интерфейсом.MediaAdapter.cs: Создайте интерфейс и адаптер в том же пространстве имен. Определите интерфейсIMediaPlayerс методомPlay(string filename), который возвращает строку. Затем создайте классMediaPlayerAdapter, реализующийIMediaPlayer. Ваш адаптер должен оборачивать экземплярLegacyAudioPlayer(переданный через конструктор) и транслировать вызовPlayв методPlayMp3устаревшего плеера.Program.cs: Объедините все вместе, создав устаревший плеер, обернув его в адаптер и используя адаптер через интерфейсIMediaPlayer. Это демонстрирует, как клиентский код может работать с современным интерфейсом, в то время как адаптер берет на себя взаимодействие с устаревшей системой за кулисами.
Вы получите один входной параметр:
- Имя файла для воспроизведения (например,
song.mp3)
Создайте LegacyAudioPlayer, оберните его в MediaPlayerAdapter, затем вызовите Play у адаптера, используя интерфейс IMediaPlayer, и выведите результат.
Например, если на вход подается favorite_track.mp3, результат должен быть:
Playing MP3: favorite_track.mp3Прелесть этого паттерна в том, что ваш основной код взаимодействует только с IMediaPlayer — он и понятия не имеет, что всю реальную работу выполняет устаревшая аудиобиблиотека. Если позже вам понадобится поддержка другой устаревшей библиотеки, вы просто создадите новый адаптер, не меняя клиентский код!
Шпаргалка
Паттерн Адаптер (Adapter) — это структурный паттерн, который позволяет несовместимым интерфейсам работать вместе, выступая в качестве моста между двумя классами.
Этот паттерн преобразует интерфейс одного класса в интерфейс, который ожидают клиенты. Он полезен, когда вы хотите использовать существующий класс, но его интерфейс не соответствует потребностям вашего кода.
Базовая структура:
// Существующий класс с несовместимым интерфейсом (Adaptee)
public class OldPrinter
{
public void PrintMessage(string msg) => Console.WriteLine($"Old: {msg}");
}
// Целевой интерфейс, который ожидает ваш код
public interface IPrinter
{
void Print(string text);
}
// Адаптер устраняет разрыв
public class PrinterAdapter : IPrinter
{
private OldPrinter _oldPrinter;
public PrinterAdapter(OldPrinter oldPrinter)
{
_oldPrinter = oldPrinter;
}
public void Print(string text)
{
_oldPrinter.PrintMessage(text); // Делегирует выполнение старому интерфейсу
}
}Использование:
OldPrinter legacy = new OldPrinter();
IPrinter printer = new PrinterAdapter(legacy);
printer.Print("Hello"); // Old: HelloКлиентский код работает с целевым интерфейсом, не подозревая о том, что за кулисами реальную работу выполняет несовместимый класс. Этот паттерн особенно ценен при интеграции сторонних библиотек или устаревшего кода.
Попробуйте сами
using System;
using MediaSystem;
class Program
{
public static void Main(string[] args)
{
// Считать ввод имени файла
string filename = Console.ReadLine();
// TODO: Создать экземпляр LegacyAudioPlayer
// TODO: Обернуть его в MediaPlayerAdapter
// TODO: Использовать адаптер через интерфейс IMediaPlayer
// и вывести результат вызова Play с именем файла
}
}
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Объектно-ориентированное программирование
1Основы ООП
Внешние файлыПространства имен и директивыВведение в классы и объектыКлючевое слово 'this'Методы и параметрыПоля и свойстваКонструкторыИнициализаторы объектовИтоги — Простой калькулятор4Наследование
Основы синтаксиса наследования (:)Ключевое слово 'base'Ключевые слова Virtual и OverrideЗапечатанные классыБазовый класс 'object'Итоги — Иерархия сотрудников7Продвинутые возможности
Перегрузка операторовИндексаторы (this[])Переопределение ToString()Методы расширенияПовторение — Пользовательский список10Паттерны проектирования. Часть 1
Введение в паттерны проектированияПотокобезопасный SingletonПаттерн FactoryПаттерн Observer (События)Паттерн Strategy13Финальные испытания
Платформа для онлайн-обученияКомпонент игрового персонажаСервис аренды транспорта2Свойства и статические члены
Автоматически реализуемые свойстваСвойства только для чтения и записиСтатические поля и методыСтатические классыЧлены в виде выражений5Полиморфизм и интерфейсы
Полиморфизм: компиляция vs выполнениеИнтерфейс vs абстрактный классМножественные интерфейсыЯвные интерфейсыUpcasting и DowncastingИтоги: Калькулятор фигур8Продвинутые концепции ООП
Композиция вместо наследованияGenerics (классы и методы)Делегаты и событияАтрибуты и рефлексияIDisposable и оператор usingОсновы Dependency Injection11Паттерны проектирования. Часть 2
Паттерн КомандаПаттерн АдаптерПаттерн ДекораторПаттерн Шаблонный методПаттерн СостояниеПаттерн Компоновщик3Архитектура классов
Экземплярные и статические данныеКлючевые слова 'readonly' и 'const'Опорные поляПовторение — Менеджер банковских счетов6Инкапсуляция
Модификаторы доступаСвойства для инкапсуляцииРеализация скрытия данныхПаттерны неизменяемостиИтоги — Записи студентов9Переменное количество аргументов
Ключевое слово 'params'Необязательные параметрыИменованные аргументыПерегрузка методов