Menu
Coddy logo textTech

Паттерн «Адаптер»

Часть раздела Объектно-ориентированное программирование путешествия по 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

Паттерн Адаптер неоценим при интеграции устаревшего кода или сторонних библиотек без изменения их исходного кода. Клиент не подозревает, что он работает с адаптированным классом — он просто использует знакомый ему интерфейс.

challenge icon

Задание

Легко

Давайте создадим систему преобразования температуры, используя паттерн Адаптер (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) для форматирования
    }
}
quiz iconПроверьте себя

В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.

Все уроки раздела Объектно-ориентированное программирование