Low Level Design

Adapter Pattern — Bridge Incompatible Interfaces in Java

How the Adapter pattern wraps legacy or third-party classes to make them work with your system's interface — without rewriting existing code.

August 26, 2026·8 min read

What is the Adapter Pattern?#

The Adapter is a structural design pattern that allows objects with incompatible interfaces to collaborate. It acts as a wrapper that translates one interface into another that clients expect.

Think of it as a plug adapter — the underlying device is the same, but the adapter makes it compatible with a different socket.

You reach for Adapter when:

  • You want to reuse an existing class but its interface doesn't match what you need
  • You need to integrate legacy or third-party code with a new system
  • You want to make unrelated classes work together without modifying either

Real-World Analogy#

Your laptop has a USB-C port, but your headphones have a 3.5mm jack. You can't plug the jack directly into the USB-C port. So you use a USB-C to 3.5mm adapter:

  • 🎧 Headphones → 3.5mm plug
  • 🔌 Adapter → converts 3.5mm to USB-C
  • 💻 Laptop → USB-C input

The headphones didn't change. The laptop didn't change. The adapter bridges the gap.


Class Diagram#


Violation Code — The Problem#

java
// Your new system expects this interface
class AudioPlayer {
    public void play(String audioData) {
        System.out.println("Playing: " + audioData);
    }
}

// Legacy code with a completely different interface
class LegacySpeaker {
    public void makeSound(String data) {
        System.out.println("Making sound: " + data);
    }
}

// ❌ Client can't use LegacySpeaker through AudioPlayer interface
public class Main {
    public static void main(String[] args) {
        AudioPlayer player = new AudioPlayer();
        player.play("song.mp3");

        LegacySpeaker legacy = new LegacySpeaker();
        legacy.makeSound("old.wav"); // completely separate — no polymorphism
    }
}

Issues:

  1. Incompatible interface — LegacySpeaker can't be used where AudioPlayer is expected
  2. Code duplication — you'd have to duplicate LegacySpeaker's logic in AudioPlayer
  3. Violates OCP — you'd need to modify AudioPlayer to accommodate the legacy code
  4. Tight coupling — incompatible components can't be swapped

Enhanced Code — Adapter Pattern#

java
// Target interface — what your system expects
public interface AudioPlayer {
    void play(String audioData);
}

// Adaptee — existing class with an incompatible interface
public class LegacySpeaker {
    public void makeSound(String data) {
        System.out.println("Legacy Speaker: " + data);
    }
}

// Adapter — wraps LegacySpeaker and implements AudioPlayer
public class SpeakerAdapter implements AudioPlayer {
    private final LegacySpeaker legacySpeaker;

    public SpeakerAdapter(LegacySpeaker speaker) {
        this.legacySpeaker = speaker;
    }

    @Override
    public void play(String audioData) {
        // Translate the call — map play() to makeSound()
        legacySpeaker.makeSound(audioData);
    }
}

// Client — only knows AudioPlayer, unaware of LegacySpeaker
public class Main {
    public static void main(String[] args) {
        AudioPlayer player = new SpeakerAdapter(new LegacySpeaker());
        player.play("song.mp3"); // ✅ works seamlessly
    }
}

LegacySpeaker was never modified. Main is completely decoupled from it.


Common LLD Problems Using Adapter Pattern#

1. Payment Gateway Integration#

  • Adapters: StripeAdapter, PayPalAdapter, RazorpayAdapter
  • Context: Each gateway has a different API; adapters unify them behind a common PaymentProcessor interface.

2. Third-Party Social Media APIs#

  • Adapters: TwitterAdapter, FacebookAdapter, LinkedInAdapter
  • Context: Each platform provides different SDKs; adapters provide a unified posting interface.

3. Legacy System Wrapping#

  • Adapters: LegacyOrderServiceAdapter
  • Context: Wrap outdated systems so they work with modern interfaces without a big rewrite.

4. Logging Framework Integration#

  • Adapters: Log4jAdapter, SLF4JAdapter, ConsoleLoggerAdapter
  • Context: Integrate various logging frameworks through a consistent application-wide logger interface.

5. Media Player — Multiple Formats#

  • Adapters: MP4Adapter, VLCAdapter, AVIAdapter
  • Context: Each format is handled through an adapter that conforms to a common MediaPlayer interface.

6. Database Abstraction Layer#

  • Adapters: MySQLAdapter, PostgresAdapter, MongoDBAdapter
  • Context: Provide a unified query interface across different databases.

7. File Format Conversion#

  • Adapters: DocxToPDFAdapter, CSVToJSONAdapter
  • Context: Convert data between formats using a common processing interface.

8. Shipping Provider Integration#

  • Adapters: FedExAdapter, UPSAdapter, BlueDartAdapter
  • Context: Normalise communication with different shipping APIs behind a standard interface.

Adapter vs Facade#

AdapterFacade
IntentMake one incompatible interface workSimplify a complex subsystem
Interface changeTranslates an existing oneCreates a new simplified one
Number of classesTypically wraps one classTypically wraps many classes

ReferencesLinks
Article ReferenceRefactoring Guru — Adapter