Object Adapter
Object Adapter uses composition to wrap the adaptee and translate interface calls.
Structure
┌──────────────────────────┐
│ Target (interface) │
├──────────────────────────┤
│ + request(): Result │
└──────────┬───────────────┘
│ implemented by
┌──────────┴───────────────┐
│ Adapter │
├──────────────────────────┤
│ - adaptee: Adaptee │ ← composition
├──────────────────────────┤
│ + request(): Result │ ← translates
└──────────┬───────────────┘
│ wraps
┌──────────┴───────────────┐
│ Adaptee (class) │
├──────────────────────────┤
│ + specificRequest(): void│
└──────────────────────────┘
Implementation
// Target interface (what client expects)
public interface PaymentGateway {
PaymentResult charge(Money amount, PaymentInfo info);
boolean refund(String transactionId, Money amount);
}
// Adaptee (existing class with incompatible interface)
public class StripeAPI {
public ChargeResponse createCharge(double amount, String currency,
String source) {
// Stripe-specific implementation
return new ChargeResponse("ch_123", "succeeded");
}
public RefundResponse createRefund(String chargeId, double amount) {
// Stripe-specific implementation
return new RefundResponse("re_456", "succeeded");
}
}
// Adapter
public class StripeAdapter implements PaymentGateway {
private final StripeAPI stripe;
public StripeAdapter(StripeAPI stripe) {
this.stripe = stripe;
}
@Override
public PaymentResult charge(Money amount, PaymentInfo info) {
// Translate from target to adaptee
ChargeResponse response = stripe.createCharge(
amount.getAmount(),
amount.getCurrency(),
info.getSource()
);
// Translate from adaptee to target
return new PaymentResult(
response.getStatus().equals("succeeded"),
response.getId()
);
}
@Override
public boolean refund(String transactionId, Money amount) {
RefundResponse response = stripe.createRefund(
transactionId,
amount.getAmount()
);
return response.getStatus().equals("succeeded");
}
}
// Client uses target interface
PaymentGateway gateway = new StripeAdapter(new StripeAPI());
PaymentResult result = gateway.charge(new Money(100, "USD"), paymentInfo);
Data Translation
// Adapter translates between different data formats
public class WeatherAdapter implements StandardWeatherService {
private final ExternalWeatherAPI externalApi;
@Override
public WeatherData getWeather(String city) {
// Get from external API (different format)
ExternalResponse response = externalApi.query(city);
// Translate to our format
return new WeatherData(
response.getTemperature(),
response.getHumidity(),
translateCondition(response.getCode())
);
}
private Condition translateCondition(int code) {
return switch (code) {
case 0 -> Condition.CLEAR;
case 1, 2, 3 -> Condition.CLOUDY;
case 45, 48 -> Condition.FOGGY;
default -> Condition.UNKNOWN;
};
}
}
Class Adapter
Class Adapter uses multiple inheritance to inherit from both the target and the adaptee.
Structure
┌──────────────────────────┐
│ Target (interface) │
└──────────┬───────────────┘
│ implemented by
┌──────────┴───────────────┐
│ Adapter │
│ (inherits both) │
└──────────┬───────────────┘
│ inherits from
┌──────────┴───────────────┐
│ Adaptee (class) │
└──────────────────────────┘
Java Implementation (via interface + class)
// Target interface
public interface MediaPlayer {
void play(String filename);
}
// Adaptee class
public class VLCPlayer {
public void playVLC(String file) {
System.out.println("Playing with VLC: " + file);
}
}
// Class Adapter (Java doesn't support multiple inheritance)
// Use interface inheritance + class inheritance
public class VLCAdapter extends VLCPlayer implements MediaPlayer {
@Override
public void play(String filename) {
playVLC(filename); // Calls inherited method
}
}
C++ Multiple Inheritance Example
// C++ supports multiple inheritance
class MediaPlayer {
public:
virtual void play(const string& file) = 0;
};
class VLCPlayer {
public:
void playVLC(const string& file) {
cout << "Playing with VLC: " << file << endl;
}
};
class VLCAdapter : public MediaPlayer, public VLCPlayer {
public:
void play(const string& file) override {
playVLC(file); // Calls VLCPlayer method
}
};
Object vs Class Adapter
| Aspect | Object Adapter | Class Adapter |
|---|---|---|
| Mechanism | Composition | Inheritance |
| Flexibility | Can adapt multiple adaptees | Fixed to one adaptee |
| Override | Cannot override adaptee | Can override adaptee methods |
| Coupling | Looser coupling | Tighter coupling |
| Language | Any OOP language | Languages with multiple inheritance |
When to Use Each
| Use Object Adapter | Use Class Adapter |
|---|---|
| Need to adapt at runtime | Adaptee is known at compile time |
| Multiple adaptees possible | Need to override adaptee behavior |
| Prefer composition | Language supports multiple inheritance |
Use Cases
Real-world applications of the Adapter pattern.
Third-Party API Integration
// Your system uses this interface
public interface EmailService {
void send(String to, String subject, String body);
}
// Third-party has different API
public class SendGridAPI {
public SendResponse sendEmail(SendGridMessage message) { ... }
}
public class MailgunAPI {
public MailResponse send(MailgunMessage message) { ... }
}
// Adapters for each provider
public class SendGridAdapter implements EmailService {
private final SendGridAPI sendGrid;
public void send(String to, String subject, String body) {
SendGridMessage msg = new SendGridMessage(to, subject, body);
sendGrid.sendEmail(msg);
}
}
public class MailgunAdapter implements EmailService {
private final MailgunAPI mailgun;
public void send(String to, String subject, String body) {
MailgunMessage msg = new MailgunMessage(to, subject, body);
mailgun.send(msg);
}
}
Database Adapter
// Unified query interface
public interface DatabaseAdapter {
List<Map<String, Object>> query(String sql, Object... params);
int execute(String sql, Object... params);
}
// MySQL adapter
public class MySQLAdapter implements DatabaseAdapter {
private final MySqlConnection conn;
public List<Map<String, Object>> query(String sql, Object... params) {
MySQLResultSet rs = conn.executeQuery(sql, params);
return convertToMapList(rs);
}
}
// PostgreSQL adapter
public class PostgresAdapter implements DatabaseAdapter {
private final PostgresConnection conn;
public List<Map<String, Object>> query(String sql, Object... params) {
PostgresResultSet rs = conn.executeQuery(sql, params);
return convertToMapList(rs);
}
}
Legacy System Integration
// Modern interface
public interface OrderProcessor {
OrderResult process(Order order);
}
// Legacy system with different interface
public class LegacyOrderSystem {
public LegacyResponse submitLegacyOrder(LegacyOrderData data) { ... }
}
// Adapter bridges old and new
public class LegacyOrderAdapter implements OrderProcessor {
private final LegacyOrderSystem legacySystem;
public OrderResult process(Order order) {
// Convert to legacy format
LegacyOrderData legacyData = convertToLegacy(order);
// Call legacy system
LegacyResponse response = legacySystem.submitLegacyOrder(legacyData);
// Convert response back
return convertFromLegacy(response);
}
}
Adapter vs Other Patterns
| Pattern | Use When |
|---|---|
| Adapter | Interface incompatibility |
| Facade | Simplify complex subsystem |
| Decorator | Add behavior to existing object |
| Proxy | Control access to object |
| Bridge | Decouple abstraction from implementation |
Practice Problems
Design a scalable Adapter Pattern system. Cover high-level architecture, data model, and API design.
Solution
// Complete system design:
// - Functional + Non-functional requirements
// - Capacity estimation
// - Data model (SQL/NoSQL choice)
// - API endpoints
// - Component architecture
// - Scaling strategy
// - Monitoring & reliabilityHow would you scale Adapter Pattern to handle 10x the current load? Identify bottlenecks and solutions.
Solution
// Scaling approach:
// 1. Load balancing
// 2. Database sharding/replication
// 3. Cache layer (Redis)
// 4. CDN for static assets
// 5. Async processing (queues)
// 6. Microservices decompositionAnalyze potential failure modes for Adapter Pattern and design mitigation strategies.
Solution
// Failure mitigation:
// 1. Redundancy (multi-AZ)
// 2. Circuit breakers
// 3. Retry with backoff
// 4. Dead letter queues
// 5. Health checks
// 6. Graceful degradationQuiz
1. What does the Adapter pattern do?
2. What is the difference between Object and Class Adapter?
3. When should you use an Adapter?
4. What is the main disadvantage of Class Adapter?
5. Adapter vs Facade?
Flashcards
Question
What is the Adapter pattern?
Click to reveal answer
Answer
Converts one interface to another, allowing classes with incompatible interfaces to work together. Uses composition or inheritance.
Question
Object Adapter vs Class Adapter?
Click to reveal answer
Answer
Object: uses composition (wraps adaptee). Class: uses inheritance (extends both). Object is more flexible; Class can override adaptee.
Question
When to use Adapter?
Click to reveal answer
Answer
Integrating third-party APIs, legacy system integration, database abstraction, or any time you need to convert between incompatible interfaces.
Question
Adapter vs Facade?
Click to reveal answer
Answer
Adapter: converts one interface to another. Facade: simplifies complex subsystem with new simplified interface.
Question
Adapter vs Decorator?
Click to reveal answer
Answer
Adapter: changes interface. Decorator: adds behavior while maintaining same interface.
Revision Notes
Key Takeaways
- 1.Adapter converts one interface to another for compatibility
- 2.Object Adapter uses composition; Class Adapter uses inheritance
- 3.Adapter is essential for integrating third-party and legacy systems
- 4.Adapter translates data formats between incompatible interfaces
- 5.Choose Object Adapter for flexibility, Class Adapter for overriding
Interview Tips
- •Show Adapter pattern when discussing third-party API integration
- •Explain data translation between different formats
- •Discuss when to use Adapter vs Facade
- •Mention Adapter for legacy system integration
Cheat Sheet
Adapter Pattern - Cheat Sheet
Purpose:
Convert one interface to another for compatibility.
Types:
| Type | Mechanism | Java Support |
|---|---|---|
| Object | Composition | ✓ |
| Class | Multiple inheritance | ✗ |
Structure:
Client → Target → Adapter → Adaptee
Use Cases:
- Third-party API integration
- Legacy system integration
- Database abstraction
- Different format conversion
Adapter vs Others:
| Pattern | Use When |
|---|---|
| Adapter | Interface conversion |
| Facade | Simplify complex API |
| Decorator | Add behavior |
| Proxy | Control access |