DEV Community

Cover image for Adapter Design Pattern in Java – A Complete Guide
ZeeshanAli-0704
ZeeshanAli-0704

Posted on

Adapter Design Pattern in Java – A Complete Guide

πŸ”Œ Adapter Design Pattern in Java – A Complete Guide

πŸ“š Table of Contents


βœ… What is the Adapter Design Pattern?

The Adapter Pattern is a structural design pattern that allows objects with incompatible interfaces to work together by converting one interface into another the client expects.

Intent: Bridge the gap between a new interface and an existing (often legacy) implementation.

This pattern acts as a wrapper around an existing class, exposing a new interface to the client.


πŸ‘₯ Key Participants

Role Description
Target The interface expected by the client.
Adaptee The existing class that needs adapting.
Adapter Implements the Target interface and translates calls to the Adaptee.
Client Uses the Target interface to interact with the system.

🧠 Real world Analogy

Think about a mobile charger adapter. Your laptop may have a USB-C port, but your phone uses Micro-USB. The adapter converts the USB-C output to Micro-USB input so your phone can charge β€” even though the two interfaces don’t match.


πŸ“Š UML Diagram (Text Format)

     +--------------+
     |   Client     |
     +--------------+
           |
           v
     +--------------+       +---------------+
     |   Target     |<------+   Adapter     |
     +--------------+       +---------------+
                            | - adaptee     |
                            +---------------+
                            | +request()    |
                            +---------------+
                                    |
                                    v
                            +---------------+
                            |   Adaptee     |
                            +---------------+
                            | +specificRequest() |
                            +---------------+
Enter fullscreen mode Exit fullscreen mode

πŸ’» Java Example

πŸ“˜ Scenario: You have a legacy payment system (OldPaymentGateway) and want to integrate it with your new PaymentProcessor interface.


1. Target Interface

public interface PaymentProcessor {
    void pay(String amount);
}
Enter fullscreen mode Exit fullscreen mode

2. Adaptee Class (Existing API)

public class OldPaymentGateway {
    public void makePayment(String amountInRupees) {
        System.out.println("Payment made using OldPaymentGateway: β‚Ή" + amountInRupees);
    }
}
Enter fullscreen mode Exit fullscreen mode

3. Adapter Class

public class PaymentAdapter implements PaymentProcessor {
    private OldPaymentGateway oldPaymentGateway;

    public PaymentAdapter(OldPaymentGateway oldPaymentGateway) {
        this.oldPaymentGateway = oldPaymentGateway;
    }

    @Override
    public void pay(String amount) {
        // Adapting method call
        oldPaymentGateway.makePayment(amount);
    }
}
Enter fullscreen mode Exit fullscreen mode

4. Client Code

public class Main {
    public static void main(String[] args) {
        OldPaymentGateway oldGateway = new OldPaymentGateway();
        PaymentProcessor processor = new PaymentAdapter(oldGateway);

        processor.pay("2500");
    }
}
Enter fullscreen mode Exit fullscreen mode

Output:

Payment made using OldPaymentGateway: β‚Ή2500
Enter fullscreen mode Exit fullscreen mode

πŸ›  Use Cases in Real World Systems

  • Integrating legacy systems with modern interfaces
  • Adapting third-party APIs (e.g., wrapping Stripe's API with your own payment layer)
  • Making incompatible class libraries work together
  • Wrapping classes with different naming conventions or method signatures
  • Supporting backward compatibility

βœ… Advantages

  • Promotes code reusability by allowing integration of old components
  • Improves interoperability between systems
  • Provides flexibility in evolving interfaces
  • Encourages decoupling between client and implementation

❌ Disadvantages

  • Can increase complexity with too many adapters
  • Might introduce runtime overhead with deep adapter chains
  • Sometimes hides the true power of the Adaptee
  • Tight coupling to Adaptee may remain if not designed carefully

🧭 When to Use and Not to Use

βœ… Use When:

  • You want to integrate an existing class but its interface doesn’t match
  • You’re using third-party or legacy code
  • You’re refactoring to support a new interface gradually

❌ Avoid When:

  • You can directly modify the existing class
  • You don’t control the Adaptee and behavior is unpredictable
  • Adapter logic becomes too complex β€” consider Facade instead

πŸ” Object Adapter vs Class Adapter

Type Description
Object Adapter Uses composition (holds Adaptee instance). More flexible.
Class Adapter Uses inheritance (extends Adaptee). Tightly coupled, limited to single inheritance.

Java only supports Object Adapters as multiple inheritance is not allowed.


πŸ” Comparison with Similar Patterns

Pattern Purpose
Adapter Convert one interface to another.
Decorator Add behavior dynamically without changing interface.
Facade Simplify a complex subsystem with a unified interface.
Proxy Provide a surrogate or placeholder.

⚠️ Best Practices and Pitfalls

βœ… Best Practices:

  • Name adapters clearly (e.g., PaymentAdapter, LegacyToNewAdapter)
  • Make adapters lightweight
  • Favor object composition over inheritance
  • Use interfaces to abstract clients from concrete adapters

❌ Pitfalls:

  • Don't create adapter chains (adapter over adapter)
  • Avoid adapting too many unrelated interfaces β€” use Facade instead
  • Don’t tightly couple your Adapter to internal logic of Adaptee

πŸ”„ Alternatives

Alternative Use When
Wrapper/Wrapper Class Simple alias or name change of methods
Facade Pattern When you need to simplify multiple interfaces
Strategy Pattern When you need interchangeable algorithms
Bridge Pattern To decouple abstraction from implementation

πŸ“Œ Summary

  • The Adapter Pattern bridges incompatible interfaces.
  • Useful for integrating legacy or third-party APIs.
  • Involves Target, Adaptee, Adapter, and Client.
  • Java supports Object Adapters (via composition).
  • Be cautious of complexity and avoid overusing adapters.
  • Best used when refactoring or integrating external libraries.

--

πŸ“š Explore More Design Patterns in Java


More Details:

Get all articles related to system design
Hastag: SystemDesignWithZeeshanAli

systemdesignwithzeeshanali

Git: https://github.com/ZeeshanAli-0704/SystemDesignWithZeeshanAli

Top comments (0)