Adapter Pattern: Common Interview Questions
Interviewers test your understanding of Adapter Pattern through design scenarios, trade-off discussions, and machine coding challenges. Here are the most common question patterns and how to approach them.
Question 1: Design a Logger System with Multiple Backends
Problem: Your application needs to log messages to different backends (Console, File, CloudWatch, Splunk). Each backend has a different API. Design a unified logging interface.
Approach:
First, define a target interface Logger with log(level, message). Second, create adapters for each backend: ConsoleLoggerAdapter, FileLoggerAdapter, CloudWatchAdapter. Third, each adapter wraps the backend-specific API and translates log() calls to the appropriate backend method. Fourth, use a Factory Pattern to instantiate the correct adapter based on configuration.
+ log(level: LogLevel, message: String): void
class CloudWatchAdapter implements Logger:
- cloudWatch: CloudWatchAPI
+ log(level, message):
severity = mapLevelToSeverity(level)
cloudWatch.putLogEvent(severity, message, timestamp)
class FileLoggerAdapter implements Logger:
- fileWriter: FileWriter
+ log(level, message):
formatted = formatMessage(level, message)
fileWriter.write(formatted)
Question 2: Adapter vs Bridge Pattern
Problem: Explain when you would use Adapter Pattern versus Bridge Pattern with concrete examples.
Answer Structure:
Intent Difference: Adapter is applied retroactively to make incompatible interfaces work together. Bridge is designed upfront to decouple abstraction from implementation so both can vary independently.
Adapter Example: You integrate a third-party SMS service (Twilio) that has sendSMS(to, from, body) into your notification system that expects send(recipient, message). You create TwilioAdapter to translate between the two interfaces. You did not design Twilio's API, you are adapting it.
Bridge Example: You design a shape rendering system where shapes (Circle, Square) can be rendered on different platforms (Windows, Linux). You create a Renderer interface and pass it to shape constructors. Shapes delegate rendering to the injected renderer. Both shape hierarchy and renderer hierarchy can evolve independently. You designed this from the beginning.
Question 3: Machine Coding - Parking Lot with Multiple Payment Processors
Problem: Design a parking lot system where users can pay via Cash, Card, or Mobile Wallet. Each payment method has different processing logic. Implement using Adapter Pattern.
Key Design Decisions:
First, define PaymentMethod interface with processPayment(amount): PaymentResult. Second, create adapters for each method: CashPaymentAdapter (no external API, just records transaction), CardPaymentAdapter (adapts card processor API), WalletAdapter (adapts PayTM/Google Pay API). Third, in ParkingTicket class, accept PaymentMethod interface and call processPayment() without knowing the concrete implementation.
+ processPayment(amount: Money): PaymentResult
class CardPaymentAdapter implements PaymentMethod:
- cardProcessor: CardProcessorAPI
- card: CreditCard
+ processPayment(amount):
if not validateCard(card):
return PaymentResult.FAILED
response = cardProcessor.charge(card.number, amount.cents)
return convertResponse(response)
class ParkingTicket:
- paymentMethod: PaymentMethod
+ checkout():
amount = calculateFee()
result = paymentMethod.processPayment(amount)
if result.isSuccess():
markAsPaid()
Follow-up Questions to Expect:
First, how do you handle payment failures and retries? (Add retry logic in adapters or create a RetryablePaymentAdapter decorator). Second, how do you handle refunds? (Add refund() to PaymentMethod interface, implement in each adapter). Third, how do you handle partial payments? (Modify interface to accept paid amount and return remaining balance, but clarify if this is a requirement before implementing).
Question 4: Two-Way Adapter
Problem: Can an adapter work both ways? Explain with an example.
Answer: Yes, a Two-Way Adapter (also called Bidirectional Adapter) implements both interfaces and delegates calls in both directions. This is useful when two subsystems need to collaborate but have incompatible interfaces.
Example: In a library management system, you have a legacy BookInventory system and a new CatalogService. The legacy system uses findByISBN() while the new system uses searchByIdentifier(). A two-way adapter implements both interfaces and translates calls bidirectionally. However, this is rare in practice because it tightly couples two systems. Usually, you pick one interface as the target and adapt everything to it.