Thilan Dissanayaka Software Architecture Apr 26

Adapter Pattern explained simply

Ever needed to connect two incompatible interfaces without changing their source code?
That’s exactly where the Adapter Pattern shines!

The Adapter Pattern is a structural design pattern that allows objects with incompatible interfaces to work together. It acts like a bridge between two different interfaces.

What is the Adapter Pattern?

At its core, the Adapter Pattern:

  • Converts the interface of a class into another interface the client expects.
  • Allows classes to work together that otherwise couldn't due to incompatible interfaces.
  • Promotes code reusability and flexibility.

In simple words:

It’s like a translator who helps two people speaking different languages communicate!

A Real-Life Analogy

Imagine you have a laptop charger with a US plug, but you're traveling in Europe where sockets are different.
You don't throw away your charger — you use a power adapter!

Similarly, in software, instead of rewriting code, we adapt it.

A Quick Example (Without Adapter)

Suppose you have an OldPrinter class:

public class OldPrinter {
    public void printDocument(String text) {
        System.out.println("Printing document: " + text);
    }
}

Now you build a new system that expects every printer to have a print() method instead:

public interface Printer {
    void print(String text);
}

Problem: OldPrinter doesn't match the new Printer interface.
You can’t directly use OldPrinter in your new system.

Enter the Adapter Pattern

We create an Adapter class that implements the Printer interface and uses an instance of OldPrinter internally.

public class PrinterAdapter implements Printer {
    private OldPrinter oldPrinter;

    public PrinterAdapter(OldPrinter oldPrinter) {
        this.oldPrinter = oldPrinter;
    }

    @Override
    public void print(String text) {
        oldPrinter.printDocument(text);
    }
}

Now, you can use OldPrinter seamlessly in the new system!

public class MainProgram {
    public static void main(String[] args) {
        OldPrinter oldPrinter = new OldPrinter();
        Printer printer = new PrinterAdapter(oldPrinter);

        printer.print("Hello, World!");
    }
}

Output:

Printing document: Hello, World!

Key Components

  • Target Interface (Printer): The interface your client code expects.
  • Adaptee (OldPrinter): The existing class that needs adapting.
  • Adapter (PrinterAdapter): Bridges the gap between the Target and Adaptee.

Types of Adapter Pattern

There are mainly two ways to implement Adapter Pattern:

1. Class Adapter (using Inheritance)

  • Adapter extends Adaptee and implements the Target interface.
  • Not very flexible because Java supports single inheritance only.

2. Object Adapter (using Composition) ⭐

  • Adapter has an instance of Adaptee.
  • More flexible and preferred in most cases.

Note: The example above is an Object Adapter.

When to Use the Adapter Pattern?

  • When you want to use an existing class but its interface doesn't match your needs.
  • When you want to create a reusable class that cooperates with unrelated classes.
  • When you need to work with legacy code without modifying it.

Real World Use Cases

  • Legacy system integration: Adapting old APIs to work with modern ones.
  • Third-party library integration: Adapting library classes to your own interfaces.
  • UI component libraries: Adapting different UI components under a single standard.

Advantages

✅ Promotes code reuse.
✅ Makes incompatible classes work together.
✅ Follows the Open/Closed Principle — open for extension but closed for modification.

Disadvantages

❌ Increases code complexity due to additional classes.
❌ Overuse can lead to too many adapters, making the code harder to maintain.

Final Thoughts

The Adapter Pattern is like a universal connector in software development.
It allows your systems to evolve without needing risky and expensive rewrites.

Whenever you encounter a mismatch between interfaces, think:

"Can I just adapt it instead of rewriting it?"

Happy coding! 🚀

ALSO READ
Blockchain 0x100 – Developing the Core Blockchain Structure
Nov 06 Web3 Development

If you are into Web3 development, the term blockchain is not a new thing to you. In the last blog post of this series, I explained most of the core concepts behind how a blockchain works. Learning....

ACID Properties in Databases: The Key to Reliable Transactions
Apr 25 Database Systems

When working with databases, one thing is absolutely critical: keeping your data safe, consistent, and reliable. That's where ACID properties come in — a set of principles that ensure every....

CSRF - Cross Site Request Forgery
May 27 Application Security

Cross-Site Request Forgery (CSRF) is a web security vulnerability that allows an attacker to induce users to perform actions that they do not intend to perform. It occurs when a malicious website,....

XSS - The Ultimate guide for Cross Site Scripting
May 27 Application Security

Cross-Site Scripting (XSS) is one of the most prevalent and dangerous web application security vulnerabilities. According to OWASP, XSS consistently ranks among the top 10 web application security....

Understanding Assembly Language: Purpose and Structure
Mar 23 Low level Development

Assembly language is a low-level programming language that provides a human-readable representation of a computer's binary instructions. Unlike high-level languages like C, C++, or Python, which are....

GraphQL - Interview preparation guide
Oct 01 Interview Guides

## What is GraphQL? GraphQL is a query language for APIs and a runtime for executing those queries. It allows clients to request exactly the data they need, reducing over-fetching and....