Adapter Pattern
Adapter Pattern: Making Incompatible Interfaces Compatible
Section titled “Adapter Pattern: Making Incompatible Interfaces Compatible”Now let’s dive into the Adapter Pattern - one of the most commonly used structural design patterns.
Why Adapter Pattern?
Section titled “Why Adapter Pattern?”Imagine you’re traveling to Europe with a US phone charger. The plug doesn’t fit European sockets! You need an adapter to make your US plug work with European sockets. The Adapter Pattern works the same way!
The Adapter Pattern lets incompatible interfaces work together. It acts as a bridge between two incompatible interfaces, allowing them to collaborate without modifying their source code.
What’s the Use of Adapter Pattern?
Section titled “What’s the Use of Adapter Pattern?”The Adapter Pattern is useful when:
- You need to use existing classes - But their interfaces don’t match what you need
- Integrating third-party libraries - Library interface doesn’t match your code
- Legacy code integration - Old code needs to work with new code
- Multiple incompatible interfaces - Need to make them work together
- You want to avoid modifying - Don’t want to change existing code
What Happens If We Don’t Use Adapter Pattern?
Section titled “What Happens If We Don’t Use Adapter Pattern?”Without the Adapter Pattern, you might:
- Modify existing code - Risk breaking working code
- Create wrapper code - Scattered throughout your codebase
- Tight coupling - Code depends on incompatible interfaces
- Code duplication - Similar conversion logic repeated everywhere
- Violate Open/Closed Principle - Need to modify code to integrate new systems
Simple Example: The Media Player
Section titled “Simple Example: The Media Player”Let’s start with a super simple example that anyone can understand!
Visual Representation
Section titled “Visual Representation”Interaction Flow
Section titled “Interaction Flow”Here’s how the Adapter Pattern works in practice - showing how incompatible interfaces are made compatible:
sequenceDiagram
participant Client
participant MediaPlayer as MediaPlayer Interface
participant Adapter as MediaAdapter
participant Mp4Player
participant VlcPlayer
Client->>MediaPlayer: play("mp4", "movie.mp4")
activate MediaPlayer
MediaPlayer->>Adapter: play("mp4", "movie.mp4")
activate Adapter
Adapter->>Adapter: Check audio_type
Adapter->>Mp4Player: play_mp4("movie.mp4")
activate Mp4Player
Mp4Player-->>Adapter: Playing MP4
deactivate Mp4Player
Adapter-->>MediaPlayer: Success
deactivate Adapter
MediaPlayer-->>Client: Media playing
deactivate MediaPlayer
Note over Client,VlcPlayer: Adapter converts interface<br/>to make it compatible!
Client->>MediaPlayer: play("vlc", "video.vlc")
activate MediaPlayer
MediaPlayer->>Adapter: play("vlc", "video.vlc")
activate Adapter
Adapter->>Adapter: Check audio_type
Adapter->>VlcPlayer: play_vlc("video.vlc")
activate VlcPlayer
VlcPlayer-->>Adapter: Playing VLC
deactivate VlcPlayer
Adapter-->>MediaPlayer: Success
deactivate Adapter
MediaPlayer-->>Client: Media playing
deactivate MediaPlayer
The Problem
Section titled “The Problem”You’re building a media player application. You have an existing Mp3Player that works great, but you need to support Mp4Player and VlcPlayer which have different interfaces. Without Adapter Pattern:
Problems:
- Client needs to know about all different player interfaces
- Different method names for each player type
- Hard to add new players - need to modify existing code
- Violates Open/Closed Principle
The Solution: Adapter Pattern
Section titled “The Solution: Adapter Pattern”Class Structure
Section titled “Class Structure”classDiagram
class MediaPlayer {
<<interface>>
+play(audio_type, filename) void
}
class Mp3Player {
+play_mp3(filename) void
}
class Mp4Player {
+play_mp4(filename) void
}
class VlcPlayer {
+play_vlc(filename) void
}
class MediaAdapter {
-mp4Player: Mp4Player
-vlcPlayer: VlcPlayer
+play(audio_type, filename) void
}
class AudioPlayer {
-adapter: MediaAdapter
+play(audio_type, filename) void
}
MediaPlayer <|.. Mp3Player : implements
MediaPlayer <|.. MediaAdapter : implements
MediaAdapter --> Mp4Player : adapts
MediaAdapter --> VlcPlayer : adapts
AudioPlayer --> MediaPlayer : uses
note for MediaAdapter "Converts incompatible\ninterfaces to compatible ones"
note for Mp4Player "Incompatible interface"
note for VlcPlayer "Incompatible interface"
Real-World Software Example: Payment Gateway Integration
Section titled “Real-World Software Example: Payment Gateway Integration”Now let’s see a realistic software example - integrating multiple payment gateways with different interfaces into a unified payment system.
The Problem
Section titled “The Problem”You’re building an e-commerce system that needs to support multiple payment gateways (Stripe, PayPal, Square). Each gateway has a different interface. Without Adapter Pattern:
Problems:
- Client needs to know about all different gateway interfaces
- Different method names and parameters for each gateway
- Hard to add new gateways - need to modify existing code
- Payment data format differs for each gateway
The Solution: Adapter Pattern
Section titled “The Solution: Adapter Pattern”Adapter Pattern Variants
Section titled “Adapter Pattern Variants”There are two main ways to implement the Adapter Pattern:
1. Object Adapter (Composition)
Section titled “1. Object Adapter (Composition)”Uses composition - adapter contains an instance of the adaptee:
Pros: More flexible, can adapt multiple adaptees
Cons: Requires adaptee instance
2. Class Adapter (Inheritance)
Section titled “2. Class Adapter (Inheritance)”Uses inheritance - adapter extends both target and adaptee:
Pros: No need for adaptee instance
Cons: Less flexible, language-dependent (Java doesn’t support multiple inheritance)
When to Use Adapter Pattern?
Section titled “When to Use Adapter Pattern?”Use Adapter Pattern when:
✅ Integrating third-party libraries - Library interface doesn’t match your code
✅ Legacy code integration - Old code needs to work with new code
✅ Multiple incompatible interfaces - Need to make them work together
✅ You want to avoid modifying - Don’t want to change existing code
✅ Unified interface needed - Want consistent interface for similar functionality
When NOT to Use Adapter Pattern?
Section titled “When NOT to Use Adapter Pattern?”Don’t use Adapter Pattern when:
❌ You can modify the code - If you can change the incompatible interface, do that instead
❌ Simple interface mismatch - If mismatch is trivial, direct modification might be better
❌ Over-engineering - Don’t add complexity for simple cases
❌ Performance critical - Adapter adds a layer of indirection (usually negligible)
Common Mistakes to Avoid
Section titled “Common Mistakes to Avoid”Mistake 1: Modifying Existing Code Instead of Adapting
Section titled “Mistake 1: Modifying Existing Code Instead of Adapting”Mistake 2: Creating Adapters for Simple Cases
Section titled “Mistake 2: Creating Adapters for Simple Cases”Mistake 3: Not Handling Data Transformation
Section titled “Mistake 3: Not Handling Data Transformation”Benefits of Adapter Pattern
Section titled “Benefits of Adapter Pattern”- Reusability - Use existing classes without modification
- Flexibility - Easy to add new adapters for new incompatible classes
- Separation of Concerns - Adapter handles compatibility, client stays clean
- Open/Closed Principle - Open for extension (new adapters), closed for modification
- Legacy Integration - Integrate old code with new code easily
Revision: Quick Catch-Up
Section titled “Revision: Quick Catch-Up”What is Adapter Pattern?
Section titled “What is Adapter Pattern?”Adapter Pattern is a structural design pattern that allows incompatible interfaces to work together. It acts as a bridge between two incompatible interfaces.
Why Use It?
Section titled “Why Use It?”- ✅ Integrate incompatible interfaces - Make old and new code work together
- ✅ Use third-party libraries - Library interface doesn’t match your code
- ✅ Avoid modifying code - Don’t want to change existing working code
- ✅ Unified interface - Want consistent interface for similar functionality
- ✅ Legacy integration - Integrate legacy systems with new systems
How It Works?
Section titled “How It Works?”- Define target interface - What the client expects
- Identify adaptee - The incompatible class
- Create adapter - Implements target interface, wraps adaptee
- Transform calls - Adapter converts target calls to adaptee calls
- Client uses adapter - Client uses unified interface
Key Components
Section titled “Key Components”Client → Target Interface → Adapter → Adaptee- Target Interface - What the client expects
- Adaptee - The incompatible class
- Adapter - Bridges target and adaptee
- Client - Uses the target interface
Simple Example
Section titled “Simple Example”class Target: def request(self): pass
class Adaptee: def specific_request(self): pass
class Adapter(Target): def __init__(self): self.adaptee = Adaptee()
def request(self): return self.adaptee.specific_request() # Adapt!When to Use?
Section titled “When to Use?”✅ Integrating third-party libraries
✅ Legacy code integration
✅ Multiple incompatible interfaces
✅ Want to avoid modifying existing code
✅ Need unified interface
When NOT to Use?
Section titled “When NOT to Use?”❌ You can modify the code directly
❌ Simple interface mismatch
❌ Over-engineering simple cases
❌ Performance critical (adds indirection)
Key Takeaways
Section titled “Key Takeaways”- Adapter Pattern = Makes incompatible interfaces compatible
- Target = Interface client expects
- Adaptee = Incompatible class
- Adapter = Bridge between target and adaptee
- Benefit = Reuse code without modification
- Use Case = Third-party libraries, legacy integration
Common Pattern Structure
Section titled “Common Pattern Structure”class Target: def request(self): pass
class Adaptee: def specific_request(self): pass
class Adapter(Target): def __init__(self): self.adaptee = Adaptee()
def request(self): return self.adaptee.specific_request()
# Usageadapter = Adapter()adapter.request() # Works!Remember
Section titled “Remember”- Adapter Pattern bridges incompatible interfaces
- It allows reuse without modification
- Use Object Adapter (composition) in most cases
- Don’t use it for simple cases - avoid over-engineering
- It’s about compatibility, not just wrapping!
Interview Focus: Adapter Pattern
Section titled “Interview Focus: Adapter Pattern”Key Points to Remember
Section titled “Key Points to Remember”1. Core Concept
Section titled “1. Core Concept”What to say:
“Adapter Pattern is a structural design pattern that allows incompatible interfaces to work together. It acts as a bridge between two incompatible interfaces, allowing them to collaborate without modifying their source code.”
Why it matters:
- Shows you understand the fundamental purpose
- Demonstrates knowledge of when to use it
- Indicates you can explain concepts clearly
2. When to Use Adapter Pattern
Section titled “2. When to Use Adapter Pattern”Must mention:
- ✅ Integrating third-party libraries - Library interface doesn’t match your code
- ✅ Legacy code integration - Old code needs to work with new code
- ✅ Multiple incompatible interfaces - Need to make them work together
- ✅ Avoid modifying code - Don’t want to change existing code
Example scenario to give:
“I’d use Adapter Pattern when integrating a third-party payment gateway like Stripe into my e-commerce system. Stripe has its own interface (
charge_stripe()), but my system expects a unified payment interface (process_payment()). I create a StripeAdapter that implements my payment interface and internally calls Stripe’s methods.”
3. Object Adapter vs Class Adapter
Section titled “3. Object Adapter vs Class Adapter”Must discuss:
- Object Adapter: Uses composition, more flexible
- Class Adapter: Uses inheritance, language-dependent
- Preference: Object Adapter is preferred in most cases
Example to give:
“I prefer Object Adapter because it uses composition, making it more flexible. I can adapt multiple adaptees or change behavior at runtime. Class Adapter uses inheritance, which is less flexible and doesn’t work well in languages like Java that don’t support multiple inheritance.”
4. Benefits and Trade-offs
Section titled “4. Benefits and Trade-offs”Benefits to mention:
- Reusability - Use existing classes without modification
- Flexibility - Easy to add new adapters
- Separation of Concerns - Adapter handles compatibility
- Open/Closed Principle - Open for extension, closed for modification
Trade-offs to acknowledge:
- Complexity - Adds a layer of indirection
- Performance - Small overhead (usually negligible)
- Over-engineering risk - Can be overkill for simple cases
5. Common Interview Questions
Section titled “5. Common Interview Questions”Q: “What’s the difference between Adapter Pattern and Decorator Pattern?”
A:
“Adapter Pattern changes the interface of an object to make it compatible with another interface. Decorator Pattern adds new behavior to an object without changing its interface. Adapter is about compatibility, Decorator is about adding functionality.”
Q: “When would you use Adapter vs modifying the code directly?”
A:
“I use Adapter when I can’t or shouldn’t modify the code - like third-party libraries, legacy systems, or when modifying would break existing functionality. If I own the code and modification is safe, I might modify directly. But Adapter is safer and follows Open/Closed Principle.”
Q: “How does Adapter Pattern relate to SOLID principles?”
A:
“Adapter Pattern primarily supports the Open/Closed Principle - you can extend functionality (add new adapters) without modifying existing code. It also supports Single Responsibility Principle by separating the adaptation logic into its own class. Additionally, it helps with Dependency Inversion Principle by allowing clients to depend on abstractions (target interface) rather than concrete implementations.”
Interview Checklist
Section titled “Interview Checklist”Before your interview, make sure you can:
- Define Adapter Pattern clearly in one sentence
- Explain when to use it (with examples)
- Describe Object Adapter vs Class Adapter
- Implement Adapter Pattern from scratch
- Compare with other structural patterns (Decorator, Facade)
- List benefits and trade-offs
- Identify common mistakes
- Give 2-3 real-world examples
- Connect to SOLID principles
- Discuss when NOT to use it
Remember: Adapter Pattern is about making incompatible interfaces work together - bridging the gap between old and new, third-party and your code! 🔌