Home/Design Patterns/Adapter Pattern

Adapter Pattern

🎯 Explain Like I'm 5...

Imagine you have a really cool American toy that needs American batteries (big round ones), but you only have European batteries (small square ones) at home! 🔋

😟 The Problem:

  • Your toy: Needs big round American batteries
  • Your batteries: Small square European batteries
  • They don't fit! The toy won't work! 😢

💡 The Solution - Use an Adapter!

  • Get a battery adapter! 🔌
  • Put your European batteries IN the adapter
  • Put the adapter IN your toy
  • Now it works! The adapter TRANSLATES between the two! 🎉

🌟 The Key Idea:

An adapter makes two incompatible things work together! Just like a power adapter lets you plug your laptop into different wall sockets around the world! 🌍

🚀 Why Is This Pattern Useful?

  • Make old code work with new code without changing it!
  • Connect systems that weren't designed to work together!
  • Use existing classes even if their interface doesn't match what you need!

📋 Pattern Purpose

The Adapter pattern converts the interface of a class into another interface that clients expect. It allows classes to work together that couldn't otherwise because of incompatible interfaces.

⚡ When to Use Adapter Pattern

  • You want to use an existing class, but its interface doesn't match what you need
  • You need to create a reusable class that cooperates with unrelated classes
  • You want to use several existing subclasses, but adapting their interface by subclassing is impractical
  • You're working with legacy code that can't be modified

🔄 Types of Adapters

Class Adapter (Inheritance):

Uses multiple inheritance to adapt one interface to another

Object Adapter (Composition):

Uses composition to wrap the adaptee - more flexible and commonly used

💻 Java Implementations

Example 1: Media Player Adapter

Adapting different audio formats to work with a standard media player interface.

MediaPlayer.java
java
1
2
3
4
// Target Interface - what we want
public interface MediaPlayer {
void play(String audioType, String fileName);
}
AdvancedMediaPlayer.java
java
1
2
3
4
5
// Adaptee Interface - what we have (incompatible)
public interface AdvancedMediaPlayer {
void playVlc(String fileName);
void playMp4(String fileName);
}
VlcPlayer.java
java
1
2
3
4
5
6
7
8
9
10
11
12
// Concrete Adaptee 1
public class VlcPlayer implements AdvancedMediaPlayer {
@Override
public void playVlc(String fileName) {
System.out.println("Playing VLC file: " + fileName);
}
@Override
public void playMp4(String fileName) {
// Do nothing
}
}
Mp4Player.java
java
1
2
3
4
5
6
7
8
9
10
11
12
// Concrete Adaptee 2
public class Mp4Player implements AdvancedMediaPlayer {
@Override
public void playVlc(String fileName) {
// Do nothing
}
@Override
public void playMp4(String fileName) {
System.out.println("Playing MP4 file: " + fileName);
}
}
MediaAdapter.java
java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// Adapter - makes AdvancedMediaPlayer compatible with MediaPlayer
public class MediaAdapter implements MediaPlayer {
private AdvancedMediaPlayer advancedPlayer;
public MediaAdapter(String audioType) {
if (audioType.equalsIgnoreCase("vlc")) {
advancedPlayer = new VlcPlayer();
} else if (audioType.equalsIgnoreCase("mp4")) {
advancedPlayer = new Mp4Player();
}
}
@Override
public void play(String audioType, String fileName) {
if (audioType.equalsIgnoreCase("vlc")) {
advancedPlayer.playVlc(fileName);
} else if (audioType.equalsIgnoreCase("mp4")) {
advancedPlayer.playMp4(fileName);
}
}
}
AudioPlayer.java
java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// Client - uses the adapter
public class AudioPlayer implements MediaPlayer {
private MediaAdapter mediaAdapter;
@Override
public void play(String audioType, String fileName) {
// Built-in support for mp3
if (audioType.equalsIgnoreCase("mp3")) {
System.out.println("Playing MP3 file: " + fileName);
}
// Use adapter for other formats
else if (audioType.equalsIgnoreCase("vlc") ||
audioType.equalsIgnoreCase("mp4")) {
mediaAdapter = new MediaAdapter(audioType);
mediaAdapter.play(audioType, fileName);
} else {
System.out.println("Invalid media type: " + audioType);
}
}
public static void main(String[] args) {
AudioPlayer player = new AudioPlayer();
player.play("mp3", "song.mp3");
player.play("mp4", "video.mp4");
player.play("vlc", "movie.vlc");
player.play("avi", "film.avi");
}
}

Example 2: Payment Gateway Adapter

Adapting different payment processors to a common interface.

PaymentProcessor.java
java
1
2
3
4
5
// Target Interface
public interface PaymentProcessor {
void processPayment(double amount);
boolean validatePayment();
}
PayPalService.java
java
1
2
3
4
5
6
7
8
9
10
11
// Adaptee 1 - PayPal (incompatible interface)
public class PayPalService {
public void sendPayment(double amount) {
System.out.println("Processing $" + amount + " via PayPal");
}
public boolean checkAccount() {
System.out.println("Validating PayPal account...");
return true;
}
}
StripeService.java
java
1
2
3
4
5
6
7
8
9
10
11
// Adaptee 2 - Stripe (different incompatible interface)
public class StripeService {
public void makePayment(double amount) {
System.out.println("Processing $" + amount + " via Stripe");
}
public boolean verifyCard() {
System.out.println("Verifying Stripe card...");
return true;
}
}
PayPalAdapter.java
java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// Adapter for PayPal
public class PayPalAdapter implements PaymentProcessor {
private PayPalService payPalService;
public PayPalAdapter(PayPalService payPalService) {
this.payPalService = payPalService;
}
@Override
public void processPayment(double amount) {
payPalService.sendPayment(amount);
}
@Override
public boolean validatePayment() {
return payPalService.checkAccount();
}
}
StripeAdapter.java
java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// Adapter for Stripe
public class StripeAdapter implements PaymentProcessor {
private StripeService stripeService;
public StripeAdapter(StripeService stripeService) {
this.stripeService = stripeService;
}
@Override
public void processPayment(double amount) {
stripeService.makePayment(amount);
}
@Override
public boolean validatePayment() {
return stripeService.verifyCard();
}
}
PaymentDemo.java
java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// Client code
public class PaymentDemo {
public static void main(String[] args) {
// Use PayPal through adapter
PaymentProcessor paypal = new PayPalAdapter(new PayPalService());
if (paypal.validatePayment()) {
paypal.processPayment(100.0);
}
System.out.println();
// Use Stripe through adapter
PaymentProcessor stripe = new StripeAdapter(new StripeService());
if (stripe.validatePayment()) {
stripe.processPayment(200.0);
}
// Both use the same interface!
processPayment(paypal, 50.0);
processPayment(stripe, 75.0);
}
// Method works with any payment processor!
private static void processPayment(PaymentProcessor processor, double amount) {
System.out.println("\nProcessing payment...");
if (processor.validatePayment()) {
processor.processPayment(amount);
}
}
}

🌍 Real-World Examples

  • 🔌Power Adapters: Convert electrical plugs between countries
  • 💳Card Readers: Adapt memory cards to USB interface
  • 🗄️JDBC: Java Database Connectivity adapts different databases
  • 🔗API Wrappers: Adapt third-party APIs to your application's interface
  • 🏛️Legacy System Integration: Make old systems work with new ones

✅ Benefits

  • Single Responsibility: Separates interface conversion from business logic
  • Open/Closed Principle: Add new adapters without changing existing code
  • Reusability: Allows incompatible classes to work together
  • Flexibility: Easy to add new adapters for new requirements

⚠️ Drawbacks

  • ⚠️Complexity: Increases number of classes and interfaces
  • ⚠️Overhead: May add slight performance overhead
  • ⚠️Indirection: Extra layer between client and adaptee

🔑 Key Points to Remember

  • 1️⃣Adapter converts one interface to another
  • 2️⃣Use composition (object adapter) rather than inheritance when possible
  • 3️⃣Makes existing classes work without modifying their source code
  • 4️⃣Different from Decorator - Adapter changes interface, Decorator adds behavior
  • 5️⃣Different from Facade - Adapter wraps one object, Facade wraps many

💪 Practice Scenarios

  • Create an adapter for different authentication systems (OAuth, LDAP, API Key)
  • Build adapters for different file formats (CSV, JSON, XML) to common data interface
  • Implement adapters for different notification services (Email, SMS, Push)
  • Design adapters for different logging frameworks
  • Create adapters for different image formats to a common image interface