Menu
Coddy logo textTech

Adapter Pattern

Part of the Object Oriented Programming section of Coddy's Java journey — lesson 71 of 87.

The Adapter Pattern is a structural design pattern that allows incompatible interfaces to work together. It acts as a bridge between two classes, converting the interface of one class into an interface that the client expects. Think of it like a power adapter that lets you plug a European device into an American outlet.

Imagine you have an existing MediaPlayer interface that plays audio files, but you need to integrate a third-party library that uses a completely different interface for playing videos:

// Existing interface your code uses
interface MediaPlayer {
    void play(String filename);
}

// Third-party class with incompatible interface
class AdvancedVideoPlayer {
    public void playVideo(String filename) {
        System.out.println("Playing video: " + filename);
    }
}

The adapter wraps the incompatible class and implements the expected interface, translating calls between them:

class VideoPlayerAdapter implements MediaPlayer {
    private AdvancedVideoPlayer videoPlayer;
    
    public VideoPlayerAdapter() {
        this.videoPlayer = new AdvancedVideoPlayer();
    }
    
    public void play(String filename) {
        videoPlayer.playVideo(filename);
    }
}

Now your client code can use the video player through the familiar MediaPlayer interface:

MediaPlayer player = new VideoPlayerAdapter();
player.play("movie.mp4");  // Output: Playing video: movie.mp4

The Adapter Pattern is invaluable when integrating legacy code or third-party libraries without modifying their source code. The client remains unaware that it's working with an adapted class—it simply uses the interface it knows.

challenge icon

Challenge

Easy

Let's build a temperature conversion system using the Adapter Pattern! You have an existing weather application that works with Celsius temperatures, but you need to integrate a legacy temperature sensor that only outputs readings in Fahrenheit. Instead of modifying either system, you'll create an adapter that bridges the gap.

You'll organize your code across four files:

  • TemperatureProvider.java: Define the interface that your weather application expects. It should declare a method getTemperatureCelsius() that returns a double representing the temperature in Celsius.
  • FahrenheitSensor.java: This represents the legacy sensor with an incompatible interface. Create a class with a private double field for the temperature in Fahrenheit, set through the constructor. It should have a method readFahrenheit() that returns the stored Fahrenheit value.
  • SensorAdapter.java: Create the adapter that makes the Fahrenheit sensor compatible with your weather application. Your adapter should implement TemperatureProvider and wrap a FahrenheitSensor. When getTemperatureCelsius() is called, it should read from the sensor and convert the value to Celsius using the formula: (fahrenheit - 32) * 5 / 9.
  • Main.java: Bring everything together! You'll receive one input: a temperature reading in Fahrenheit (double).

    Create a FahrenheitSensor with the input value. Then create a SensorAdapter that wraps this sensor. Store the adapter in a TemperatureProvider variable to demonstrate that your weather application can use it through the expected interface.

    Print the result in this format: Temperature: [celsius] C where celsius is formatted to one decimal place.

You will receive one input: the Fahrenheit temperature (double).

For example, with input 98.6, your output would be:

Temperature: 37.0 C

Notice how your Main class works entirely with the TemperatureProvider interface—it has no idea that the actual temperature is coming from a Fahrenheit sensor being adapted behind the scenes. This is the elegance of the Adapter Pattern!

Cheat sheet

The Adapter Pattern is a structural design pattern that allows incompatible interfaces to work together by acting as a bridge between two classes, converting one interface into another that the client expects.

Key Components

  • Target Interface: The interface that the client expects to use
  • Adaptee: The existing class with an incompatible interface
  • Adapter: The class that implements the target interface and wraps the adaptee, translating calls between them

Basic Structure

// Target interface
interface MediaPlayer {
    void play(String filename);
}

// Adaptee (incompatible class)
class AdvancedVideoPlayer {
    public void playVideo(String filename) {
        System.out.println("Playing video: " + filename);
    }
}

// Adapter
class VideoPlayerAdapter implements MediaPlayer {
    private AdvancedVideoPlayer videoPlayer;
    
    public VideoPlayerAdapter() {
        this.videoPlayer = new AdvancedVideoPlayer();
    }
    
    public void play(String filename) {
        videoPlayer.playVideo(filename);
    }
}

// Client usage
MediaPlayer player = new VideoPlayerAdapter();
player.play("movie.mp4");  // Output: Playing video: movie.mp4

When to Use

The Adapter Pattern is useful when:

  • Integrating legacy code or third-party libraries without modifying their source code
  • Making incompatible interfaces work together
  • The client needs to remain unaware of the adapted implementation

Try it yourself

import java.util.Scanner;

class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        double fahrenheit = scanner.nextDouble();
        
        // TODO: Create a FahrenheitSensor with the input value
        
        // TODO: Create a SensorAdapter that wraps the sensor
        
        // TODO: Store the adapter in a TemperatureProvider variable
        
        // TODO: Get the temperature in Celsius and print it
        // Format: "Temperature: [celsius] C" with one decimal place
        // Hint: Use String.format("%.1f", value) for formatting
    }
}
quiz iconTest yourself

This lesson includes a short quiz. Start the lesson to answer it and track your progress.

All lessons in Object Oriented Programming