Design Patterns interview question · Most asked
What is the Adapter pattern? Give an example.
Summary
Adapter wraps a class that has an incompatible interface and translates calls to the interface your code expects. For example, a LegacyBillingAdapter implements your PaymentProvider.charge(cents) by converting cents to a dollar string and calling the legacy submit().
Longer Answer
The client sees only the target interface; all translation (method names, units, result shapes, error types) lives in the adapter. That keeps vendor-specific details out of business logic and makes it easy to switch or add providers.
Real examples: Laravel's filesystem adapters (local, S3, FTP behind one API), Java's InputStreamReader and Arrays.asList(), DB-API drivers in Python.
Code Example
class LegacyBilling: # third-party: amount as a dollar string
def submit(self, amount):
return {"ref": f"LB-{amount}"}
class BillingAdapter: # our interface: charge(cents) -> id
def __init__(self, legacy):
self.legacy = legacy
def charge(self, cents):
return self.legacy.submit(f"{cents // 100}.{cents % 100:02d}")["ref"]
print(BillingAdapter(LegacyBilling()).charge(1250))
class LegacyBilling { // third-party: amount as a dollar string
submit(amount: string) { return { ref: `LB-${amount}` }; }
}
interface PaymentProvider { charge(cents: number): string; }
class BillingAdapter implements PaymentProvider {
constructor(private legacy: LegacyBilling) {}
charge(cents: number) {
return this.legacy.submit(`${Math.floor(cents / 100)}.${String(cents % 100).padStart(2, "0")}`).ref;
}
}
console.log(new BillingAdapter(new LegacyBilling()).charge(1250));
import java.util.Map;
public class Main {
public static void main(String[] args) {
System.out.println(new BillingAdapter(new LegacyBilling()).charge(1250));
}
}
class LegacyBilling { // third-party: amount as a dollar string
Map<String, String> submit(String amount) { return Map.of("ref", "LB-" + amount); }
}
interface PaymentProvider { String charge(int cents); }
class BillingAdapter implements PaymentProvider {
private final LegacyBilling legacy;
BillingAdapter(LegacyBilling legacy) { this.legacy = legacy; }
public String charge(int cents) {
return legacy.submit(String.format("%d.%02d", cents / 100, cents % 100)).get("ref");
}
}
<?php
class LegacyBilling { // third-party: amount as a dollar string
public function submit(string $amount): array { return ["ref" => "LB-$amount"]; }
}
interface PaymentProvider { public function charge(int $cents): string; }
class BillingAdapter implements PaymentProvider {
public function __construct(private LegacyBilling $legacy) {}
public function charge(int $cents): string {
return $this->legacy->submit(sprintf("%d.%02d", intdiv($cents, 100), $cents % 100))["ref"];
}
}
echo (new BillingAdapter(new LegacyBilling()))->charge(1250), "\n";
Key Points
- Translates an incompatible interface to the expected one.
- Client stays unchanged.
- Ideal for third-party and legacy code.
- Keeps vendor types out of the domain.
Deep dive: Design Patterns, Explained Simply: Adapter
Explanation
What interviewers look for
Clear roles (target, adaptee, adapter), a realistic example, and the benefit of isolating vendor code.
Follow-up questions
- How is Adapter different from Facade?
- Class adapter vs object adapter?
More most-asked Design Patterns questions
- What problem does the Builder pattern solve? Show an example.
- Explain the SOLID principles.
- What is a design pattern, and why are they useful?
- What is the difference between dependency inversion, dependency injection and inversion of control?
- Why is composition often preferred over inheritance?
- What is dependency injection, and what forms does it take?
- Explain the Factory Method pattern and how it differs from a simple factory.
- How do you implement a Singleton, and why is it often considered an anti-pattern?