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

Паттерн «Адаптер» действует как мост между двумя несовместимыми интерфейсами и является структурным паттерном проектирования. Через посредника (адаптер) он преобразует интерфейс одного класса в другой интерфейс, ожидаемый клиентом, позволяя классам, которые изначально не могли работать вместе, взаимодействовать.

Этот паттерн включает один класс, который отвечает за объединение функциональности независимых или несовместимых интерфейсов. Реальный пример — устройство для чтения карт, которое действует как адаптер между картой памяти и ноутбуком. Вы вставляете карту памяти в устройство для чтения карт, затем вставляете устройство для чтения карт в ноутбук, чтобы карту памяти можно было прочитать через ноутбук.

Предположим, есть аудиоплеер, который может воспроизводить только файлы MP3. Теперь нам нужно воспроизводить файлы VLC и MP4. Это можно сделать, создав адаптер:

  • Целевой интерфейс: Определите интерфейс аудиоплеера, который может воспроизводить файлы в нескольких форматах.
  • Адаптируемый класс: Существующий аудиоплеер может воспроизводить только файлы MP3.
  • Класс адаптера: Создайте новый класс, который реализует целевой интерфейс, внутренне использует адаптируемый класс для воспроизведения файлов MP3 и добавляет поддержку файлов VLC и MP4.

Обзор

Паттерн «Адаптер» — это паттерн проектирования программного обеспечения, предназначенный для решения проблем совместимости между различными интерфейсами.

Назначение: Преобразовать интерфейс одного класса в другой интерфейс, чтобы изначально несовместимые классы могли работать вместе.

Основные решаемые проблемы: В программной системе необходимо поместить существующий объект в новую среду, но интерфейс, требуемый новой средой, не соответствует существующему объекту.

Сценарий использования

  • Требуется использовать существующий класс, но его интерфейс не соответствует требованиям системы.
  • Желание создать повторно используемый класс, который работает с несколькими несвязанными классами (включая классы, которые могут быть добавлены в будущем), возможно, не имеющими единого интерфейса.
  • Посредством преобразования интерфейса интегрировать один класс в другую систему классов.

Подход к реализации

  • Наследование или зависимость: Рекомендуется использовать зависимость, а не наследование, чтобы сохранить гибкость.

Ключевой код

Адаптер наследует от существующего объекта или зависит от него и реализует требуемый целевой интерфейс.

Пример приложения

  • Адаптер напряжения: Преобразовать напряжение 110V в 220V для адаптации к электрическим стандартам разных стран.
  • Преобразование интерфейса: Например, преобразование интерфейса Enumeration из Java JDK 1.1 в интерфейс Iterator из 1.2.
  • Кроссплатформенная работа: Запуск программ Windows в Linux.
  • Подключение к базе данных: В Java JDBC взаимодействует с разными типами баз данных через паттерн «Адаптер».

Преимущества

  • Способствует взаимодействию между классами, даже если они не имеют прямой связи.
  • Повышает повторное использование.
  • Повышает прозрачность класса.
  • Обеспечивает хорошую гибкость.

Недостатки

  • Чрезмерное использование паттерна Адаптер может привести к хаотичной структуре системы, которую трудно понять и поддерживать.
  • В Java, поскольку можно наследовать только один класс, можно адаптировать только один класс, и целевой класс должен быть абстрактным.

Рекомендации по использованию

  • Паттерн Адаптер следует использовать с осторожностью, особенно на этапе детального проектирования; в основном он применяется для решения проблем в существующих системах.
  • Паттерн Адаптер — подходящий выбор, когда рассматривается изменение интерфейса нормально работающей системы.

Таким образом, паттерн Адаптер может ясно выразить свои основные концепции и применения, избегая ненужной сложности.

Структура

Паттерн Адаптер содержит следующие основные роли:

  • Целевой интерфейс (Target): Определяет интерфейс, необходимый клиенту.
  • Адаптируемый класс (Adaptee): Определяет уже существующий интерфейс, который нужно адаптировать.
  • Класс адаптера (Adapter): Реализует целевой интерфейс и вызывает методы класса Adaptee через композицию или наследование, тем самым реализуя целевой интерфейс.

Реализация

У нас естьMediaPlayerинтерфейс и реализацияMediaPlayerкласс сущности интерфейсаAudioPlayer. По умолчанию,AudioPlayerможет воспроизводить аудиофайлы в формате mp3.

У нас также есть другой интерфейсAdvancedMediaPlayerи реализуетAdvancedMediaPlayerКонкретный класс реализации этого интерфейса. Этот класс может воспроизводить файлы в форматах vlc и mp4.

Мы хотим заставитьAudioPlayerвоспроизводить аудиофайлы в других форматах. Чтобы реализовать эту функциональность, нам нужно создать класс, реализующий...MediaPlayerкласс-адаптер интерфейсаMediaAdapter, и используетAdvancedMediaPlayerобъект для воспроизведения требуемого формата.

AudioPlayerИспользует класс-адаптерMediaAdapterПередаёт требуемый тип аудио; нет необходимости знать фактический класс, который может воспроизводить аудио в требуемом формате.AdapterPatternDemoИспользование классаAudioPlayerКласс для воспроизведения различных форматов.

适配器模式的 UML 图

Шаг 1

Создайте интерфейсы для медиаплеера и более продвинутого медиаплеера.

MediaPlayer.java

public interface MediaPlayer { public void play(String audioType, String fileName); }

AdvancedMediaPlayer.java

public interface AdvancedMediaPlayer { public void playVlc(String fileName); public void playMp4(String fileName); }

Шаг 2

Создайте реализациюAdvancedMediaPlayerКласс сущности интерфейса.

VlcPlayer.java

public class VlcPlayer implements AdvancedMediaPlayer{ @Override public void playVlc(String fileName) { System.out.println("Воспроизведение файла vlc. Имя: "+ fileName); } @Override public void playMp4(String fileName) { //Ничего не делать } }

Mp4Player.java

public class Mp4Player implements AdvancedMediaPlayer{ @Override public void playVlc(String fileName) { //Ничего не делать } @Override public void playMp4(String fileName) { System.out.println("Воспроизведение mp4 файла. Имя: "+ fileName); } }

Шаг 3

Создайте реализациюMediaPlayerкласса адаптера для интерфейса.

MediaAdapter.java

public class MediaAdapter implements MediaPlayer { AdvancedMediaPlayer advancedMusicPlayer; public MediaAdapter(String audioType){ if(audioType.equalsIgnoreCase("vlc") ){ advancedMusicPlayer = new VlcPlayer(); } else if (audioType.equalsIgnoreCase("mp4")){ advancedMusicPlayer = new Mp4Player(); } } @Override public void play(String audioType, String fileName) { if(audioType.equalsIgnoreCase("vlc")){ advancedMusicPlayer.playVlc(fileName); }else if(audioType.equalsIgnoreCase("mp4")){ advancedMusicPlayer.playMp4(fileName); } } }

Шаг 4

Создайте реализациюMediaPlayerкласс сущности интерфейса.

AudioPlayer.java

public class AudioPlayer implements MediaPlayer { MediaAdapter mediaAdapter; @Override public void play(String audioType, String fileName) { //Встроенная поддержка воспроизведения музыкальных файлов mp3 if(audioType.equalsIgnoreCase("mp3")){ System.out.println("Воспроизведение mp3-файла. Имя: "+ fileName); } //mediaAdapter обеспечивает поддержку воспроизведения других форматов файлов else if(audioType.equalsIgnoreCase("vlc") || audioType.equalsIgnoreCase("mp4")){ mediaAdapter = new MediaAdapter(audioType); mediaAdapter.play(audioType, fileName); } else{ System.out.println("Недопустимый медиафайл. "+ audioType + " формат не поддерживается"); } } }

Шаг 5

Используйте AudioPlayer для воспроизведения различных типов аудиоформатов.

AdapterPatternDemo.java

public class AdapterPatternDemo { public static void main(String[] args) { AudioPlayer audioPlayer = new AudioPlayer(); audioPlayer.play("mp3", "beyond the horizon.mp3"); audioPlayer.play("mp4", "alone.mp4"); audioPlayer.play("vlc", "far far away.vlc"); audioPlayer.play("avi", "mind me.avi"); } }

Шаг 6

Запустите программу, выведите результаты:

Playing mp3 file. Name: beyond the horizon.mp3
Playing mp4 file. Name: alone.mp4
Playing vlc file. Name: far far away.vlc
Invalid media. avi format not supported
Другие расширения