Skip to content

Design Pattern: Adapter (Structural)

Make an incompatible interface fit

Builds on Interfaces and Composition vs Inheritance

Stage 3core1 min read

The Interface Incompatibility Problem

The current code shows a classic mismatch. CheckoutService is flexible because it depends on the PaymentGateway interface. But it cannot use any third-party library that does not follow that exact interface.


1. Key Issues in this Design

Interface Mismatch (The "Square Peg, Round Hole" Problem)

  • The Issue: CheckoutService calls a method named pay(String, double), but RazorpayAPI offers makePayment(String, double).
  • The Impact: Both classes do the same job (take a payment), yet they cannot work together. Their "signatures" (method names and inputs) differ.

Violation of the Open/Closed Principle

  • The Issue: To use RazorpayAPI, you would have to edit the CheckoutService logic or change the RazorpayAPI source code.
  • The Impact: RazorpayAPI is often a third-party library, so you cannot edit it. Editing CheckoutService for every new provider makes the system fragile and hard to maintain.

Dependency on Concrete Implementation

  • The Issue: Without an adapter, CheckoutService cannot accept Razorpay at all. In Main, passing new RazorpayAPI() to it is a compile error.
  • The Impact: You lose the benefit of Dependency Inversion (depending on an interface, not on a concrete class). You cannot swap providers at runtime if their interfaces don't match your internal standard.

2. Structural Analysis

ComponentStatusObservation
Target (PaymentGateway)DefinedOur internal standard.
Client (CheckoutService)CompatibleWorks with any PaymentGateway.
Adaptee (RazorpayAPI)IncompatibleHas the logic we need but the wrong interface.

3. Summary of the "Smell"

When you have a useful third-party library or a legacy class that performs the required logic but has an incompatible interface, you have a structural gap. Do not rewrite your whole service to fit one vendor. Build an adapter (a thin wrapper) instead.

In this topic

Before code1 file

Before/Main.java66 linesGitHub
package DesignPatterns.StructuralPatterns.AdapterDesignPattern.Before;// 1. TARGET INTERFACE: The standard our system expects.interface PaymentGateway {    void pay(String orderId, double amount);}// 2. COMPATIBLE IMPLEMENTATION: Works perfectly with our service.class PayUGateway implements PaymentGateway {    @Override    public void pay(String orderId, double amount) {        System.out.println(            "Paid Rs. " + amount + " using PayU for order: " + orderId        );    }}/** * 3. ADAPTEE: The incompatible class. * It has the logic we want, but the interface is different. * Instead of 'pay', it uses 'makePayment'. * Instead of 'orderId', it uses 'invoiceId'. */class RazorpayAPI {    public void makePayment(String invoiceId, double amountInRupees) {        System.out.println(            "Paid Rs. " +                amountInRupees +                " using Razorpay for invoice: " +                invoiceId        );    }}/** * 4. CLIENT: The service that depends on the Target Interface. */class CheckoutService {    private PaymentGateway paymentGateway;    public CheckoutService(PaymentGateway paymentGateway) {        this.paymentGateway = paymentGateway;    }    public void checkout(String orderId, double amount) {        paymentGateway.pay(orderId, amount);    }}public class Main {    public static void main(String[] args) {        // Works fine        CheckoutService payUService = new CheckoutService(new PayUGateway());        payUService.checkout("12", 1780);        // PROBLEM: We cannot use Razorpay here!        // CheckoutService razorpayService = new CheckoutService(new RazorpayAPI());        // ^ This will cause a COMPILATION ERROR because RazorpayAPI does not        // implement the PaymentGateway interface.    }}

Done with this topic?

How well do you know it?

PreviousCreational Design Patterns: Master Comparison