Паттерн «Адаптер»
Часть раздела Объектно-ориентированное программирование путешествия по Java на Coddy — урок 71 из 87.
Паттерн Адаптер (Adapter Pattern) — это структурный паттерн проектирования, который позволяет несовместимым интерфейсам работать вместе. Он выступает в качестве моста между двумя классами, преобразуя интерфейс одного класса в интерфейс, который ожидает клиент. Представьте его как адаптер питания, который позволяет подключить европейское устройство к американской розетке.
Представьте, что у вас есть существующий интерфейс MediaPlayer, который воспроизводит аудиофайлы, но вам нужно интегрировать стороннюю библиотеку, использующую совершенно другой интерфейс для воспроизведения видео:
// Существующий интерфейс, который использует ваш код
interface MediaPlayer {
void play(String filename);
}
// Сторонний класс с несовместимым интерфейсом
class AdvancedVideoPlayer {
public void playVideo(String filename) {
System.out.println("Playing video: " + filename);
}
}Адаптер оборачивает несовместимый класс и реализует ожидаемый интерфейс, транслируя вызовы между ними:
class VideoPlayerAdapter implements MediaPlayer {
private AdvancedVideoPlayer videoPlayer;
public VideoPlayerAdapter() {
this.videoPlayer = new AdvancedVideoPlayer();
}
public void play(String filename) {
videoPlayer.playVideo(filename);
}
}Теперь ваш клиентский код может использовать видеоплеер через знакомый интерфейс MediaPlayer:
MediaPlayer player = new VideoPlayerAdapter();
player.play("movie.mp4"); // Вывод: Воспроизведение видео: movie.mp4Паттерн Адаптер неоценим при интеграции устаревшего кода или сторонних библиотек без изменения их исходного кода. Клиент не подозревает, что он работает с адаптированным классом — он просто использует знакомый ему интерфейс.
Задание
ЛегкоДавайте создадим систему преобразования температуры, используя паттерн Адаптер (Adapter Pattern)! У вас есть существующее погодное приложение, которое работает с температурой в градусах Цельсия, но вам необходимо интегрировать устаревший датчик температуры, который выдает показания только в градусах Фаренгейта. Вместо того чтобы изменять какую-либо из систем, вы создадите адаптер, который устранит этот разрыв.
Вы распределите свой код по четырем файлам:
TemperatureProvider.java: Определите интерфейс, который ожидает ваше погодное приложение. Он должен объявлять методgetTemperatureCelsius(), который возвращает значение типа double, представляющее температуру в градусах Цельсия.FahrenheitSensor.java: Этот файл представляет устаревший датчик с несовместимым интерфейсом. Создайте класс с приватным полем типа double для температуры в Фаренгейтах, устанавливаемым через конструктор. У него должен быть методreadFahrenheit(), который возвращает сохраненное значение в Фаренгейтах.SensorAdapter.java: Создайте адаптер, который сделает датчик Фаренгейта совместимым с вашим погодным приложением. Ваш адаптер должен реализовывать интерфейсTemperatureProviderи оборачивать объектFahrenheitSensor. При вызовеgetTemperatureCelsius()он должен считывать данные с датчика и преобразовывать значение в градусы Цельсия по формуле:(fahrenheit - 32) * 5 / 9.Main.java: Соберите все вместе! Вы получите один входной параметр: показание температуры в Фаренгейтах (double).Создайте объект
FahrenheitSensorс входным значением. Затем создайтеSensorAdapter, который оборачивает этот датчик. Сохраните адаптер в переменной типаTemperatureProvider, чтобы продемонстрировать, что ваше погодное приложение может использовать его через ожидаемый интерфейс.Выведите результат в формате:
"Temperature: " + String.format("%.1f", celsius) + " C", где celsius отформатирован до одного знака после запятой.
Вы получите один входной параметр: температуру в Фаренгейтах (double).
Например, при входных данных 98.6 ваш вывод будет следующим:
Temperature: 37.0 CОбратите внимание, как ваш класс Main полностью работает с интерфейсом TemperatureProvider — он и не подозревает, что фактическая температура поступает от датчика Фаренгейта, адаптируемого «за кулисами». В этом и заключается элегантность паттерна Адаптер!
Шпаргалка
Паттерн Адаптер (Adapter Pattern) — это структурный паттерн проектирования, который позволяет несовместимым интерфейсам работать вместе, выступая в качестве моста между двумя классами и преобразуя один интерфейс в другой, который ожидает клиент.
Основные компоненты
- Target Interface: Интерфейс, который клиент ожидает использовать
- Adaptee: Существующий класс с несовместимым интерфейсом
- Adapter: Класс, который реализует целевой интерфейс и оборачивает адаптируемый класс, транслируя вызовы между ними
Базовая структура
// Целевой интерфейс
interface MediaPlayer {
void play(String filename);
}
// Адаптируемый класс (несовместимый класс)
class AdvancedVideoPlayer {
public void playVideo(String filename) {
System.out.println("Playing video: " + filename);
}
}
// Адаптер
class VideoPlayerAdapter implements MediaPlayer {
private AdvancedVideoPlayer videoPlayer;
public VideoPlayerAdapter() {
this.videoPlayer = new AdvancedVideoPlayer();
}
public void play(String filename) {
videoPlayer.playVideo(filename);
}
}
// Использование клиентом
MediaPlayer player = new VideoPlayerAdapter();
player.play("movie.mp4"); // Вывод: Playing video: movie.mp4Когда использовать
Паттерн Адаптер полезен, когда:
- Интеграция устаревшего кода или сторонних библиотек без изменения их исходного кода
- Обеспечение совместной работы несовместимых интерфейсов
- Клиент не должен знать о деталях адаптированной реализации
Попробуйте сами
import java.util.Scanner;
class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
double fahrenheit = scanner.nextDouble();
// TODO: Создайте FahrenheitSensor с входным значением
// TODO: Создайте SensorAdapter, который оборачивает сенсор
// TODO: Сохраните адаптер в переменной типа TemperatureProvider
// TODO: Получите температуру в градусах Цельсия и выведите ее
// Формат: "Temperature: [celsius] C" с одним десятичным знаком
// Подсказка: Используйте String.format("%.1f", value) для форматирования
}
}
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Объектно-ориентированное программирование
1Основы ООП
Внешние файлыВведение в ООПКлассы и объектыКлючевое слово thisМетодыПоля (атрибуты)Метод-конструкторПерегрузка конструкторовИтоги — Простой калькулятор4Наследование
Основы наследования (extends)Ключевое слово superПереопределение методов (@Override)Цепочка конструкторовКласс ObjectОдиночное и многоуровневое наследованиеПочему нет множественного наследования классовИтоги — Иерархия сотрудников7Специальные методы и класс Object
Метод toString()equals() и hashCode()Метод clone()compareTo() и ComparableИнтерфейс ComparatorПовторение — Кастомная сортировка2Модификаторы доступа и инкапсуляция
Обзор уровней доступаМетоды геттеры и сеттерыСокрытие данныхКлючевое слово finalИтоги — Менеджер банковского счета5Полиморфизм
Основы перегрузки методовПереопределение методов (Run-Time)Upcasting и DowncastingОператор instanceofАбстрактные классы и методыИтоги: Калькулятор фигур8Продвинутые концепции ООП
Композиция против наследованияАгрегация против композицииВнутренние, вложенные и анонимные классыEnums и методы перечисленийRecords (Java 16+)Sealed-классы (Java 17+)11Паттерны проектирования. Часть 1
Введение в паттерны проектированияПаттерн SingletonПаттерн FactoryПаттерн BuilderПаттерн ObserverПаттерн Strategy3Свойства класса и статические члены
Переменные экземпляра и статические переменныеСтатические методыСтатические блокиКонстанты (static final)Итоги: Счетчик и утилиты6Интерфейсы и абстрактные классы
Введение в интерфейсыРеализация интерфейсовРеализация нескольких интерфейсовDefault и Static методы в интерфейсахАбстрактные классы vs ИнтерфейсыФункциональные интерфейсыИтоги — Платежная система9Обобщения
Введение в обобщенияОбобщенные классыОбобщенные методыОграниченные параметры типаСимволы подстановки (Wildcards: ?, extends, super)Итоги — Обобщенный контейнер12Паттерны проектирования. Часть 2
Паттерн «Команда»Паттерн «Адаптер»Паттерн «Декоратор»Паттерн «Шаблонный метод»Паттерн «Состояние»Паттерн «Компоновщик»Паттерн «Итератор»