Skip to Content
Course content

153: The Adapter Pattern

Click on the "Edit" button in the top corner of the screen to edit your slide content.

When do I actually need an adapter instead of just rewriting the code?

I get this question a lot. If the code is "wrong" or incompatible, why not just fix it? In a perfect world, you would. But in the real world, you're often dealing with code you cannot change. Maybe it's a third-party library you bought from a vendor, a legacy system that's too fragile to touch, or a shared module used by ten other teams who would all scream if you changed a method signature.

The Adapter pattern is your "diplomat." It allows two incompatible interfaces to talk to each other without forcing either one to change. You aren't fixing the legacy code; you're building a bridge. If you find yourself thinking, "I wish this class had a method that looked like that," you're looking at a prime candidate for an adapter.

How do I actually structure the adapter in Java?

Let's look at a concrete scenario. Suppose your app uses a standard PaymentProcessor interface. You've just integrated a new, fancy vendor called "QuickPay," but their SDK is written in a way that doesn't match your interface at all. Their method is called sendMoney(), while your system expects processPayment().

// This is what your system expects
interface PaymentProcessor {
    void processPayment(double amount);
}

// This is the third-party class you can't change
class QuickPaySDK {
    public void sendMoney(double value, String currency) {
        System.out.println("Sending " + value + " " + currency + " via QuickPay.");
    }
}

To make QuickPaySDK work with your PaymentProcessor, you create an adapter class. This class implements your interface and wraps the SDK object:

class QuickPayAdapter implements PaymentProcessor {
    private final QuickPaySDK quickPaySDK;

    public QuickPayAdapter(QuickPaySDK sdk) {
        this.quickPaySDK = sdk;
    }

    @Override
    public void processPayment(double amount) {
        // The adapter translates the call and fills in the gaps 
        // (like the currency, which our interface doesn't track)
        quickPaySDK.sendMoney(amount, "USD");
    }
}

Now, your main application logic doesn't even know QuickPaySDK exists. It just sees a PaymentProcessor and calls processPayment(). The adapter handles the translation behind the scenes.

Should I use inheritance or composition for the adapter?

You'll see two versions of this pattern in some textbooks: the "Class Adapter" (using inheritance) and the "Object Adapter" (using composition). I almost always recommend composition—which is what I used in the example above.

  • Inheritance: You extend both the adapter and the adaptee. This is restrictive in Java because we don't have multiple inheritance. If your legacy class already extends something else, you're stuck.
  • Composition: You hold a reference to the legacy object. This is way more flexible. You can swap out the adaptee at runtime, and it keeps your adapter class decoupled from the legacy implementation details.

Stick with composition. It's cleaner, it's safer, and it follows the "favor composition over inheritance" rule that will save you a lot of headaches as your project grows.




📋 Practical Task

Integrating a Legacy Temperature Sensor

You are building a climate control system that expects all sensors to implement a TemperatureSensor interface with a method double getTemperatureCelsius().

You've been handed a legacy sensor class called OldSchoolThermometer. This class is a compiled JAR and cannot be modified. It has one method: double getFahrenheit().

Your Task:

  • Create the TemperatureSensor interface.
  • Create a mock OldSchoolThermometer class that returns a hardcoded Fahrenheit value (e.g., 98.6).
  • Implement an OldSchoolAdapter that implements TemperatureSensor and wraps the OldSchoolThermometer.
  • In the adapter, implement the conversion logic: Celsius = (Fahrenheit - 32) * 5/9.
  • Write a Main class to demonstrate that you can pass the adapter into a method that expects a TemperatureSensor and receive the correct Celsius value.
Rating
0 0

There are no comments for now.

to be the first to leave a comment.