Паттерн «Адаптер»
Часть раздела Объектно-ориентированное программирование путешествия по Java на Coddy. Урок 71 из 87.
Шаблон Adapter — это структурный шаблон проектирования, который позволяет несовместимым интерфейсам работать вместе. Он действует как мост между двумя классами, преобразуя интерфейс одного класса в интерфейс, который ожидает Client. Представь его как адаптер питания, позволяющий подключить европейское устройство к американской розетке.
Представьте, что у вас есть существующий интерфейс MediaPlayer, который воспроизводит аудиофайлы, но вам нужно интегрировать стороннюю библиотеку, использующую совершенно другой интерфейс для воспроизведения видео:
// Существующий интерфейс, который использует ваш код
interface MediaPlayer {
void play(String filename);
}
// Сторонний класс с несовместимым интерфейсом
class AdvancedVideoPlayer {
public void playVideo(String filename) {
System.out.println("Playing video: " + filename);
}
}adapter оборачивает incompatible class и implements ожидаемый interface, преобразуя вызовы между ними:
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"); // Вывод: Playing video: movie.mp4Шаблон Adapter незаменим при интеграции legacy-кода или сторонних библиотек без изменения их исходного кода. Client не знает о том, что работает с адаптированным классом. Он просто использует известный ему интерфейс.
Задание
ЛегкоДавайте создадим систему преобразования температуры с использованием паттерна Adapter! У вас есть существующее погодное приложение, работающее с температурами в Celsius, но вам нужно интегрировать legacy-датчик температуры, который выдаёт показания только в Fahrenheit. Вместо изменения любой из систем вы создадите adapter, который устранит несовместимость между ними.
Вы организуете код в четырёх файлах:
TemperatureProvider.java: Define interface, которую ожидает ваше погодное приложение. Она должна Declare методgetTemperatureCelsius(), возвращающий double, представляющий температуру в Celsius.FahrenheitSensor.java: Этот класс представляет legacy-датчик с incompatible interface. Create class с private double field для температуры в Fahrenheit, задаваемым через constructor. В нём должен быть методreadFahrenheit(), возвращающий сохранённое значение в Fahrenheit.SensorAdapter.java: Create adapter, который делает FahrenheitSensor compatible с вашим погодным приложением. Ваш adapter должен implementsTemperatureProviderи оборачиватьFahrenheitSensor. При вызовеgetTemperatureCelsius()он должен считывать значение с датчика и convert его в Celsius по формуле:(fahrenheit - 32) * 5 / 9.Main.java: Объедините всё вместе! Вы получите один input: показание температуры в Fahrenheit (double).Create
FahrenheitSensorсо значением из input. Затем CreateSensorAdapter, который оборачивает этот датчик. Сохраните adapter в переменной типаTemperatureProvider, чтобы показать, что ваше погодное приложение может использовать его через ожидаемый interface.Выведите результат в таком формате:
Temperature: [celsius] C, где celsius отформатирован с точностью до одного знака после десятичной точки.
Вы получите один input: температуру в Fahrenheit (double).
Например, для input 98.6 результатом будет:
Temperature: 37.0 CОбратите внимание, что ваш Main class полностью работает с interface TemperatureProvider. Он не знает, что фактическая температура поступает от FahrenheitSensor, адаптированного за кулисами. В этом и заключается элегантность паттерна Adapter!
Попробуйте сами
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
Паттерн «Команда»Паттерн «Адаптер»Паттерн «Декоратор»Паттерн «Шаблонный метод»Паттерн «Состояние»Паттерн «Компоновщик»Паттерн «Итератор»Потренируйтесь самостоятельно: Онлайн-компилятор Java