Adapter (Adapter Pattern)
Structural design pattern enabling incompatible interfaces to work together by converting one interface into the expected format.
Updated on January 8, 2026
The Adapter pattern, also known as Wrapper, is a structural design pattern that allows classes with incompatible interfaces to work together. It acts as a translation bridge between two distinct systems, converting calls from one interface to another without modifying the existing source code. This pattern is particularly valuable when integrating third-party libraries or legacy systems into modern architectures.
Pattern Fundamentals
- Converts the interface of a class (Adaptee) into another interface (Target) expected by the client
- Enables code reuse without direct modification, respecting the Open/Closed principle
- Exists in two variants: Object Adapter (composition) and Class Adapter (multiple inheritance)
- Clearly separates business logic from interface conversion logic
Key Benefits
- Reusability: integrates existing components without modifying their source code
- Flexibility: facilitates replacement of external dependencies and system evolution
- Single Responsibility: isolates conversion logic in a dedicated class
- Compatibility: enables use of third-party libraries with different interfaces
- Testability: simplifies unit testing by allowing injection of adapted mocks
Practical Example
Consider an application that needs to integrate a third-party payment service with an interface different from what our system expects:
// Interface expected by our application
interface PaymentProcessor {
processPayment(amount: number, currency: string): Promise<PaymentResult>;
refund(transactionId: string): Promise<RefundResult>;
}
// Third-party service with incompatible interface
class StripePaymentService {
charge(cents: number, currencyCode: string, metadata: object): StripeCharge {
// Stripe logic
return { id: 'ch_123', status: 'succeeded', amount: cents };
}
createRefund(chargeId: string): StripeRefund {
return { id: 'ref_456', status: 'succeeded' };
}
}
// Adapter making Stripe compatible with our interface
class StripePaymentAdapter implements PaymentProcessor {
constructor(private stripeService: StripePaymentService) {}
async processPayment(amount: number, currency: string): Promise<PaymentResult> {
// Conversion: dollars -> cents
const cents = Math.round(amount * 100);
const charge = this.stripeService.charge(cents, currency.toUpperCase(), {});
return {
success: charge.status === 'succeeded',
transactionId: charge.id,
amount: charge.amount / 100
};
}
async refund(transactionId: string): Promise<RefundResult> {
const refund = this.stripeService.createRefund(transactionId);
return {
success: refund.status === 'succeeded',
refundId: refund.id
};
}
}
// Transparent usage
class CheckoutService {
constructor(private paymentProcessor: PaymentProcessor) {}
async checkout(cart: Cart): Promise<void> {
// Service doesn't know it's using Stripe through an adapter
const result = await this.paymentProcessor.processPayment(
cart.total,
'USD'
);
if (result.success) {
console.log('Payment successful:', result.transactionId);
}
}
}
// Configuration
const stripeService = new StripePaymentService();
const paymentAdapter = new StripePaymentAdapter(stripeService);
const checkout = new CheckoutService(paymentAdapter);Implementation Steps
- Identify the target interface (Target) expected by your client code
- Analyze the existing interface (Adaptee) that requires adaptation
- Create the Adapter class implementing the Target interface
- Inject the Adaptee instance into the Adapter's constructor
- Implement Target methods by delegating and translating to the Adaptee
- Handle necessary type conversions, formats, and naming conventions
- Test the adapter with edge cases to ensure translation robustness
Pro Tip
Prefer Object Adapter (composition) over Class Adapter (inheritance). Composition offers greater flexibility, allows adapting multiple classes simultaneously, and better respects the composition over inheritance principle. Use explicit interfaces to clearly document the expected contract.
Related Tools and Frameworks
- Spring Framework: extensively uses adapters for integrating different technologies
- NestJS: modular architecture facilitating adapter creation for external services
- TypeORM / Prisma: database adapters providing a unified interface
- Axios / Fetch Wrappers: adapters for standardizing HTTP calls
- Port-Adapter Architecture (Hexagonal): architectural approach based on adapter concepts
The Adapter pattern is a cornerstone of modern software architecture, particularly in microservices and continuous integration contexts. By isolating external dependencies behind consistent interfaces, it enables development teams to maintain stable business code while retaining the flexibility needed for technological evolution. This approach significantly reduces maintenance costs and facilitates progressive migrations to new solutions.
