Bird
Raised Fist0
LLDsystem_design~7 mins

Adapter pattern in LLD - System Design Guide

Choose your learning style10 modes available
Problem Statement
When integrating two systems or components that have incompatible interfaces, direct communication fails. This causes errors or forces rewriting existing code, increasing time and cost.
Solution
The adapter pattern acts as a translator between incompatible interfaces. It wraps one component and exposes a compatible interface so the other component can use it without changes.
Architecture
┌─────────────┐      ┌───────────────┐      ┌─────────────┐
│ Client      │─────▶│ Adapter       │─────▶│ Adaptee     │
│ (expects    │      │ (translates   │      │ (incompatible│
│ Target API) │      │ interface)    │      │ interface)  │
└─────────────┘      └───────────────┘      └─────────────┘

This diagram shows the client calling the adapter, which translates calls to the adaptee's incompatible interface.

Trade-offs
✓ Pros
→
Allows reuse of existing incompatible components without modifying them.
→
Decouples client code from specific implementations, improving flexibility.
→
Supports multiple adapters for different incompatible interfaces.
✗ Cons
→
Adds an extra layer, which can slightly impact performance.
→
Increases system complexity by introducing additional classes.
→
Requires maintenance of adapter code as interfaces evolve.
Use when you need to integrate legacy or third-party components with incompatible interfaces into your system without changing their code.
Avoid when interfaces are already compatible or when you control both sides and can refactor them directly.
Real World Examples
Stripe
Uses adapters to integrate with multiple payment gateways that have different APIs, providing a unified interface to clients.
Amazon
Applies adapter pattern to connect various internal services with legacy systems having incompatible interfaces.
LinkedIn
Uses adapters to allow new features to interact with older data storage systems without rewriting them.
Code Example
The before code fails because Client expects a 'print' method, but OldPrinter has 'print_text'. The adapter wraps OldPrinter and exposes a 'print' method that calls 'print_text', making the interfaces compatible.
LLD
### Before Adapter (incompatible interface)
class OldPrinter:
    def print_text(self, text):
        print(f"Old Printer: {text}")

class Client:
    def __init__(self, printer):
        self.printer = printer

    def print(self, message):
        # Expects method named 'print'
        self.printer.print(message)

printer = OldPrinter()
client = Client(printer)
# This will fail because OldPrinter has no 'print' method
# client.print("Hello")


### After Adapter
class PrinterAdapter:
    def __init__(self, old_printer):
        self.old_printer = old_printer

    def print(self, message):
        self.old_printer.print_text(message)

printer = OldPrinter()
adapter = PrinterAdapter(printer)
client = Client(adapter)
client.print("Hello")  # Works correctly
OutputSuccess
Alternatives
Facade pattern
Facade provides a simplified interface to a complex subsystem, while Adapter changes an interface to be compatible.
Use when: Choose Facade when you want to simplify usage of a complex system rather than adapt incompatible interfaces.
Decorator pattern
Decorator adds responsibilities to objects dynamically without changing their interface, Adapter changes the interface itself.
Use when: Choose Decorator when you want to add behavior without altering the interface.
Summary
The adapter pattern solves interface incompatibility by wrapping components with a compatible interface.
It enables reuse of existing code without modification and decouples client and service implementations.
Adapters add flexibility but introduce extra layers and maintenance overhead.

Practice

(1/5)
1. What is the main purpose of the Adapter pattern in system design?
easy
A. To improve the performance of a system by caching results
B. To allow incompatible interfaces to work together by converting one interface to another
C. To store data persistently in a database
D. To create multiple instances of a class

Solution

  1. Step 1: Understand the Adapter pattern role

    The Adapter pattern acts as a bridge between two incompatible interfaces, allowing them to communicate.
  2. Step 2: Compare with other options

    The other options describe different design goals unrelated to interface compatibility.
  3. Final Answer:

    To allow incompatible interfaces to work together by converting one interface to another -> Option B
  4. Quick Check:

    Adapter pattern = interface compatibility [OK]
Hint: Adapter = bridge between incompatible interfaces [OK]
Common Mistakes:
  • Confusing Adapter with Singleton or Factory patterns
  • Thinking Adapter improves performance directly
  • Assuming Adapter stores data
2. Which of the following is the correct way to declare an Adapter class in a typical object-oriented language?
easy
A. class Adapter implements Adaptee { private TargetInterface target; }
B. class Adapter extends Adaptee implements TargetInterface { }
C. class Adapter extends TargetInterface { private Adaptee adaptee; }
D. class Adapter implements TargetInterface { private Adaptee adaptee; }

Solution

  1. Step 1: Identify Adapter pattern structure

    The Adapter class implements the TargetInterface and holds a reference to the Adaptee to delegate calls.
  2. Step 2: Analyze options

    class Adapter implements TargetInterface { private Adaptee adaptee; } correctly shows Adapter implementing TargetInterface and containing Adaptee. The other options misuse inheritance or interfaces.
  3. Final Answer:

    class Adapter implements TargetInterface { private Adaptee adaptee; } -> Option D
  4. Quick Check:

    Adapter implements target and wraps adaptee [OK]
Hint: Adapter implements target interface, holds adaptee instance [OK]
Common Mistakes:
  • Using inheritance incorrectly instead of composition
  • Confusing which interface to implement
  • Swapping adaptee and target roles
3. Given this Adapter pattern code snippet, what will be the output?
class Adaptee {
  String specificRequest() { return "Adaptee response"; }
}

interface Target {
  String request();
}

class Adapter implements Target {
  private Adaptee adaptee = new Adaptee();
  public String request() {
    return adaptee.specificRequest();
  }
}

public class Main {
  public static void main(String[] args) {
    Target target = new Adapter();
    System.out.println(target.request());
  }
}
medium
A. Adaptee response
B. Target response
C. Compilation error
D. Runtime exception

Solution

  1. Step 1: Trace method calls in Adapter

    The Adapter's request() calls adaptee.specificRequest(), which returns "Adaptee response".
  2. Step 2: Check main method output

    Main creates Adapter as Target and prints target.request(), so output is "Adaptee response".
  3. Final Answer:

    Adaptee response -> Option A
  4. Quick Check:

    Adapter delegates request() to adaptee.specificRequest() [OK]
Hint: Adapter.request() returns adaptee.specificRequest() result [OK]
Common Mistakes:
  • Expecting 'Target response' instead of adaptee's
  • Assuming compilation or runtime errors without cause
  • Confusing method names
4. Identify the error in this Adapter pattern implementation:
interface Target {
  void request();
}

class Adaptee {
  void specificRequest() { System.out.println("Adaptee called"); }
}

class Adapter implements Target {
  Adaptee adaptee = new Adaptee();
  public void request() {
    specificRequest();
  }
}
medium
A. Adapter calls specificRequest() without referencing adaptee instance
B. Adaptee should implement Target interface
C. Adapter should extend Adaptee instead of implementing Target
D. Target interface method signature is incorrect

Solution

  1. Step 1: Review Adapter's request() method

    Adapter calls specificRequest() directly, but specificRequest() belongs to Adaptee instance, so it should call adaptee.specificRequest().
  2. Step 2: Validate other options

    Adaptee does not need to implement Target. Adapter implements Target correctly. Target interface method is correct.
  3. Final Answer:

    Adapter calls specificRequest() without referencing adaptee instance -> Option A
  4. Quick Check:

    Adapter must call adaptee.specificRequest() [OK]
Hint: Call adaptee methods via adaptee instance inside Adapter [OK]
Common Mistakes:
  • Calling adaptee methods without instance prefix
  • Misusing inheritance instead of composition
  • Changing Target interface unnecessarily
5. You need to integrate a legacy payment system with a new e-commerce platform. The legacy system has a method processLegacyPayment(), but the platform expects processPayment(). How should you apply the Adapter pattern to solve this?
hard
A. Create a new payment system and discard the legacy system
B. Modify the legacy system to rename processLegacyPayment() to processPayment()
C. Create an Adapter class implementing the platform's payment interface and inside it call processLegacyPayment() of the legacy system
D. Use inheritance to make the legacy system extend the platform's payment interface

Solution

  1. Step 1: Understand integration requirement

    The platform expects processPayment(), but legacy system only has processLegacyPayment().
  2. Step 2: Apply Adapter pattern

    Create an Adapter that implements the platform interface and inside calls legacy's processLegacyPayment() to adapt calls without changing legacy code.
  3. Step 3: Evaluate other options

    Modifying the legacy code is risky. Discarding the legacy system is costly. Using inheritance is not possible if the legacy system is not designed for it.
  4. Final Answer:

    Create an Adapter class implementing the platform's payment interface and inside it call processLegacyPayment() of the legacy system -> Option C
  5. Quick Check:

    Adapter wraps legacy method to match platform interface [OK]
Hint: Adapter wraps legacy calls to match new interface [OK]
Common Mistakes:
  • Changing legacy code directly
  • Ignoring Adapter pattern benefits
  • Trying inheritance when not supported
Recommended

Ready to prove it?

Pass the test and this pattern moves unknown strong while earning +1 Grip toward your certificate.

10 questions~5 mineasy hard