Adapter Pattern

The Adapter Pattern acts as a bridge between two incompatible interfaces and is a structural design pattern. Through an intermediary (adapter), it converts one class's interface into another interface expected by the client, enabling classes that originally could not work together to collaborate.

This pattern involves a single class that is responsible for joining independent or incompatible interface functionalities. A real-world example is a card reader, which acts as an adapter between a memory card and a laptop. You insert the memory card into the card reader, then insert the card reader into the laptop, so that the memory card can be read through the laptop.

Suppose there is an audio player that can only play MP3 files. Now, we need to play VLC and MP4 files. This can be achieved by creating an adapter:

  • Target Interface: Define an audio player interface that can play files in multiple formats.
  • Adaptee Class: The existing audio player can only play MP3 files.
  • Adapter Class: Create a new class that implements the target interface, uses the adaptee class internally to play MP3 files, and adds support for VLC and MP4 files.

Overview

The Adapter Pattern is a software design pattern aimed at solving compatibility issues between different interfaces.

Purpose: Convert one class's interface into another interface, so that originally incompatible classes can work together.

Main problems solved: In a software system, it is necessary to put an existing object into a new environment, but the interface required by the new environment does not match the existing object.

Use Case

  • An existing class needs to be used, but its interface does not meet the system requirements.
  • Hope to create a reusable class that works with multiple unrelated classes (including classes that may be introduced in the future), which may not have a unified interface.
  • Through interface conversion, integrate one class into another class system.

Implementation Approach

  • Inheritance or Dependency: It is recommended to use dependency rather than inheritance to maintain flexibility.

Key code

The adapter inherits from or depends on the existing object and implements the required target interface.

Application example

  • Voltage Adapter: Convert 110V voltage to 220V to adapt to the electrical standards of different countries.
  • Interface Conversion: For example, converting the Enumeration interface of Java JDK 1.1 to the Iterator interface of 1.2.
  • Cross-Platform Operation: Run Windows programs on Linux.
  • Database Connection: In Java, JDBC interacts with different types of databases through the Adapter Pattern.

Advantages

  • Promotes collaboration between classes, even if they have no direct association.
  • Improves class reusability.
  • Increases class transparency.
  • Provides good flexibility.

Disadvantages

  • Overuse of the adapter pattern may lead to a chaotic system structure that is difficult to understand and maintain.
  • In Java, because only one class can be inherited, only one class can be adapted, and the target class must be abstract.

Usage suggestions

  • The Adapter Pattern should be used with caution, especially in the detailed design stage; it is mostly used to solve problems in existing systems.
  • The Adapter pattern is a suitable choice when considering modifying the interface of a normally operating system.

In this way, the Adapter Pattern can clearly express its core concepts and applications while avoiding unnecessary complexity.

Structure

The Adapter Pattern contains the following main roles:

  • Target interface (Target): Defines the interface that the client needs.
  • Adaptee class (Adaptee): Define an already existing interface that needs to be adapted.
  • Adapter class (Adapter): Implement the target interface, and call the methods in the adaptee class through composition or inheritance, thereby realizing the target interface.

Implementation

We have aMediaPlayerAn interface and an implementation ofMediaPlayerEntity class of the interfaceAudioPlayer. By default,AudioPlayerCan play audio files in mp3 format.

We also have another interfaceAdvancedMediaPlayerand implementsAdvancedMediaPlayerThe concrete implementation class of the interface. This class can play files in vlc and mp4 formats.

We want to makeAudioPlayerPlay audio files in other formats. To implement this functionality, we need to create one that implements...MediaPlayerAdapter class of the interfaceMediaAdapter, and usesAdvancedMediaPlayerObject to play the required format.

AudioPlayerUsing the adapter classMediaAdapterPass the required audio type; there is no need to know the actual class that can play audio in the required format.AdapterPatternDemoClass usageAudioPlayerClass to play various formats.

适配器模式的 UML 图

Step 1

Create interfaces for the media player and the more advanced media player.

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); }

Step 2

Create an ImplementationAdvancedMediaPlayerEntity class of the interface.

VlcPlayer.java

public class VlcPlayer implements AdvancedMediaPlayer{ @Override public void playVlc(String fileName) { System.out.println("Playing vlc file. Name: "+ fileName); } @Override public void playMp4(String fileName) { //Do Nothing } }

Mp4Player.java

public class Mp4Player implements AdvancedMediaPlayer{ @Override public void playVlc(String fileName) { //Do Nothing } @Override public void playMp4(String fileName) { System.out.println("Playing mp4 file. Name: "+ fileName); } }

Step 3

Create an ImplementationMediaPlayerAdapter class for the interface.

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); } } }

Step 4

Create an ImplementationMediaPlayerEntity class of the interface.

AudioPlayer.java

public class AudioPlayer implements MediaPlayer { MediaAdapter mediaAdapter; @Override public void play(String audioType, String fileName) { //Built-in support for playing mp3 music files if(audioType.equalsIgnoreCase("mp3")){ System.out.println("Playing mp3 file. Name: "+ fileName); } //mediaAdapter provides support for playing other file formats else if(audioType.equalsIgnoreCase("vlc") || audioType.equalsIgnoreCase("mp4")){ mediaAdapter = new MediaAdapter(audioType); mediaAdapter.play(audioType, fileName); } else{ System.out.println("Invalid media. "+ audioType + " format not supported"); } } }

Step 5

Use AudioPlayer to play different types of audio formats.

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"); } }

Step 6

Run the program, output the results:

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
other extensions