Bird
Raised Fist0

Adapter Pattern - Making Incompatible Interfaces Work Together

LLDeasyInterview freq: common
Choose your preparation mode4 modes available
๐ŸŽฏ
Adapter Pattern - Making Incompatible Interfaces Work Together
40 min ยท medium
medium

You have two incompatible interfaces: LegacyPaymentProcessor and ModernPaymentGateway. LegacyPaymentProcessor has a method processLegacyPayment(String account, double amount), while ModernPaymentGateway has processPayment(PaymentRequest request). You must design an adapter to allow LegacyPaymentProcessor clients to use ModernPaymentGateway without modifying existing classes.

  • LegacyPaymentProcessor interface cannot be changed (third-party library).
  • ModernPaymentGateway interface is extensible and supports additional payment features.
  • Adapter must translate LegacyPaymentProcessor calls into ModernPaymentGateway calls.
  • Concurrency: multiple threads may invoke payment processing simultaneously.
  • PaymentRequest is a complex object with multiple fields, not just amount and account.
  • The adapter must ensure thread-safe invocation of ModernPaymentGateway.
๐Ÿ“‹
Guided Steps
1/5
Step 1
Identify the incompatible interfaces and their methods
Define the LegacyPaymentProcessor and ModernPaymentGateway interfaces with their respective methods and parameters.
๐Ÿ’ก Hint
Use Java interface keyword; interfaces cannot hold instance state or implementation in Java 8 and below.
Define LegacyPaymentProcessor and ModernPaymentGateway as Java interfaces because they specify contracts without state; interfaces cannot hold instance fields in Java.
Mid: Recognizes Adapter pattern to bridge incompatible interfaces. Senior: Explains why interfaces are used here due to lack of instance state and need for polymorphism.
โš ๏ธCommon Wrong Answer
Using abstract classes for LegacyPaymentProcessor and ModernPaymentGateway leads to unnecessary inheritance and state management complexity when only method contracts are needed.
Step 2
Design the Adapter class implementing the Legacy interface
Create an Adapter class that implements LegacyPaymentProcessor and internally uses ModernPaymentGateway to process payments.
๐Ÿ’ก Hint
Adapter holds a reference to ModernPaymentGateway (composition) and delegates calls after translating parameters.
Adapter class implements LegacyPaymentProcessor and holds a private final ModernPaymentGateway reference (composition) to delegate calls; composition lifecycle means Adapter owns the reference but ModernPaymentGateway can exist independently.
Mid: Names Adapter pattern and composition relationship. Senior: Explains lifecycle ownership and why composition is preferred over inheritance here.
โš ๏ธCommon Wrong Answer
Extending ModernPaymentGateway instead of composition breaks Liskov Substitution Principle and couples Adapter tightly to implementation.
Step 3
Implement parameter translation logic inside Adapter
Translate LegacyPaymentProcessor method parameters into a PaymentRequest object for ModernPaymentGateway.
๐Ÿ’ก Hint
Use a private method inside Adapter to convert String and double into PaymentRequest fields.
Implement private method in Adapter to convert legacy parameters into PaymentRequest; this encapsulates translation logic and respects Single Responsibility Principle.
Mid: Recognizes need for parameter translation in Adapter. Senior: Explains how encapsulating translation prevents code duplication and eases future extension.
โš ๏ธCommon Wrong Answer
Directly passing legacy parameters without translation causes runtime errors or incorrect payment processing due to incompatible method signatures.
Step 4
Ensure thread-safe payment processing in Adapter
Make Adapter's processLegacyPayment method thread-safe to handle concurrent calls.
๐Ÿ’ก Hint
Use synchronized keyword on processLegacyPayment to atomically invoke ModernPaymentGateway's processPayment, preventing race conditions.
Declare public synchronized void processLegacyPayment(String account, double amount) in Adapter to ensure atomic translation and delegation; synchronized prevents interleaving that could corrupt shared state or cause inconsistent payment processing.
Mid: Names synchronized method to ensure thread safety. Senior: Traces thread interleaving where without synchronized, two threads could corrupt shared data or cause double processing.
โš ๏ธCommon Wrong Answer
Omitting synchronization leads to race conditions where multiple threads invoke ModernPaymentGateway simultaneously causing inconsistent payment states.
Step 5
Design PaymentRequest as an immutable class
Implement PaymentRequest with final fields and no setters to ensure thread safety and immutability.
๐Ÿ’ก Hint
Use final fields and no mutator methods; provide constructor to set all fields.
PaymentRequest is a final class with private final fields and no setters, ensuring immutability and thread safety without synchronization.
Mid: Recognizes immutability for thread safety. Senior: Explains how final fields guarantee safe publication and visibility across threads without volatile or synchronization.
โš ๏ธCommon Wrong Answer
Mutable PaymentRequest with setters causes visibility issues and race conditions in concurrent environments.
๐Ÿ”Œ
API Design Challenge
Design an adapter to enable LegacyPaymentProcessor clients to use ModernPaymentGateway without modifying existing classes, ensuring thread safety and proper parameter translation.
  • Use Java interfaces to define LegacyPaymentProcessor and ModernPaymentGateway contracts without state.
  • Implement Adapter class using composition to hold a private final ModernPaymentGateway reference.
  • Synchronize the adapter's processLegacyPayment method to ensure thread-safe delegation.
๐Ÿ“Š
Scoring Rubric
โ–ผ Show
Must Have - 60%
  • LegacyPaymentProcessor and ModernPaymentGateway as Java interfaces without state
  • Adapter class implements LegacyPaymentProcessor and uses composition with ModernPaymentGateway
  • Synchronized processLegacyPayment method in Adapter for thread safety
  • Immutable PaymentRequest class with final fields
  • Encapsulation of parameter translation logic inside Adapter
Good to Have - 30%
  • Explanation of lifecycle ownership in composition relationship
  • Avoidance of inheritance from ModernPaymentGateway to respect Liskov Substitution Principle
  • Use of final keyword for Adapter's ModernPaymentGateway reference
  • Clear separation of concerns following Single Responsibility Principle
  • Discussion of safe publication guarantees from immutability
Bonus - 10%
  • Use of double-checked locking if Adapter is singleton (not required here)
  • Detailed concurrency discussion on synchronized method benefits
โœ…
Model Answer
โ–ผ Reveal

This solution implements the Adapter pattern in Java to bridge two incompatible interfaces: LegacyPaymentProcessor and ModernPaymentGateway. The Adapter class implements LegacyPaymentProcessor and internally holds a private final ModernPaymentGateway reference. It translates legacy method parameters into a PaymentRequest object and delegates calls in a thread-safe manner using synchronized methods. PaymentRequest is designed as an immutable class to ensure thread safety without synchronization overhead.

  • Use Java interfaces to define LegacyPaymentProcessor and ModernPaymentGateway contracts without state, enabling polymorphism and flexibility.
  • Implement Adapter class using composition to hold a private final ModernPaymentGateway reference, ensuring loose coupling and lifecycle management.
  • Encapsulate parameter translation logic inside a private method in Adapter to convert legacy parameters into PaymentRequest, adhering to Single Responsibility Principle.
  • Declare Adapter's processLegacyPayment method as synchronized to ensure atomic translation and delegation, preventing race conditions in concurrent environments.
  • Design PaymentRequest as a final immutable class with private final fields and no setters to guarantee thread safety and safe publication without synchronization.
  • LegacyPaymentProcessor and ModernPaymentGateway defined as Java interfaces because interfaces cannot hold instance state and define contracts.
  • Adapter class implements LegacyPaymentProcessor and holds a private final ModernPaymentGateway reference (composition) to delegate calls.
  • Adapter encapsulates parameter translation logic in a private method to convert legacy parameters into PaymentRequest.
  • Adapter's processLegacyPayment method declared synchronized to ensure atomic translation and delegation, preventing race conditions.
  • PaymentRequest designed as an immutable final class with private final fields and no setters for thread safety.
  • Use of synchronized method in Adapter ensures thread-safe invocation of ModernPaymentGateway without external locking.
  • Composition relationship between Adapter and ModernPaymentGateway means Adapter owns the reference but ModernPaymentGateway can exist independently.
  • Avoid inheritance from ModernPaymentGateway to respect Liskov Substitution Principle and maintain loose coupling.
  • Immutability of PaymentRequest ensures safe publication and visibility guarantees without volatile or synchronization.
  • Adapter pattern enforces Open/Closed Principle by allowing extension without modifying existing interfaces.

Practice

(1/5)
1. In a command pattern implementation for undo/redo, a Command interface declares an execute() method and concrete commands implement this interface to encapsulate operations. Which OOP concept is primarily applied here and why is this the correct use case?
easy
A. Encapsulation, because each command object hides the details of the operation it performs
B. Inheritance, because concrete commands extend a base class to reuse code
C. Polymorphism, because commands override execute() to provide different behaviors
D. Abstraction, because the interface hides implementation details from the client

Solution

  1. Step 1: Identify the role of Command objects

    Each command encapsulates all information needed to perform an action, hiding the operation's details from the invoker.
  2. Step 2: Differentiate encapsulation from other concepts

    While polymorphism and abstraction are involved, the key concept is encapsulation because commands package the action and its parameters as an object.
  3. Final Answer:

    Option A -> Option A
  4. Quick Check:

    Command objects encapsulate operations and data for undo/redo [check]
Hint: Command pattern encapsulates operations as objects [check]
Common Mistakes:
2. In a traffic light system design, a TrafficLightController class has a private constructor and a static synchronized getInstance() method. Which design pattern is applied here and why is this the correct choice?
easy
A. Factory pattern to create multiple controller instances safely
B. Builder pattern to construct complex traffic light states
C. Prototype pattern to clone traffic light controllers for scalability
D. Singleton pattern to ensure only one controller manages the traffic lights

Solution

  1. Step 1: Identify the pattern from constructor and method

    The private constructor prevents external instantiation, and the static synchronized getInstance() ensures a single shared instance, matching Singleton pattern characteristics.
  2. Step 2: Why Singleton fits traffic light controller

    Only one controller should coordinate the traffic lights to avoid conflicting states; multiple instances would cause inconsistent signals.
  3. Final Answer:

    Option D -> Option D
  4. Quick Check:

    Private constructor + synchronized getInstance() -> Singleton pattern [check]
Hint: Private constructor + static synchronized getInstance() -> Singleton [check]
Common Mistakes:
3. You want to add a WeekendFeeStrategy to calculate plugin usage fees on weekends without modifying existing plugin classes. Which SOLID principle guides the minimum change, and what is the correct approach?
medium
A. Open-Closed Principle: create a new WeekendFeeStrategy class implementing FeeStrategy; existing classes remain unchanged.
B. Dependency Inversion Principle: inject WeekendFeeStrategy into Plugin constructor, modifying Plugin to accept it.
C. Single Responsibility Principle: modify Plugin class to add weekend fee logic.
D. Liskov Substitution Principle: subclass Plugin to override fee calculation for weekends.

Solution

  1. Step 1: Identify the SOLID principle for adding new behavior without modifying existing code

    The Open-Closed Principle (OCP) states software entities should be open for extension but closed for modification.
  2. Step 2: Apply OCP by creating a new WeekendFeeStrategy implementing FeeStrategy interface

    This allows adding weekend fee logic without changing existing plugin or fee classes.
  3. Final Answer:

    Option A -> Option A
  4. Quick Check:

    OCP: extend with new strategy class, no existing class changes [check]
Hint: OCP: add new class, don't modify existing ones [check]
Common Mistakes:
4. You want to add a WeekendFeeStrategy to the rate limiter's fee calculation without modifying existing classes. Which SOLID principle applies and what is the minimum change?
medium
A. Add weekend flag to RateLimiter and use if-else inside existing fee method
B. Create new WeekendFeeStrategy implementing FeeStrategy interface; inject it via constructor (OCP and DIP enforced)
C. Modify RateLimiter class to add weekend fee logic directly, violating OCP
D. Create WeekendFeeStrategy but modify existing FeeStrategy implementations to handle weekend

Solution

  1. Step 1: Identify SOLID principle for adding new fee logic

    Open-Closed Principle (OCP) requires adding new behavior without modifying existing code.
  2. Step 2: Evaluate options for minimal change respecting OCP and DIP

    Add weekend flag to RateLimiter and use if-else inside existing fee method adds weekend flag and if-else inside existing method, violating OCP by modifying existing class. Create new WeekendFeeStrategy implementing FeeStrategy interface; inject it via constructor (OCP and DIP enforced) respects OCP and DIP by adding new class and injecting it.
  3. Final Answer:

    Option A -> Option A
  4. Quick Check:

    Minimal change but violates OCP by modifying existing class [check]
Hint: OCP: add new class, not modify existing code [check]
Common Mistakes:
5. In the Facebook design, the FriendRequestManager class has a sendRequest() method that checks if a friend request exists before creating one. Without synchronization, which exact thread interleaving causes duplicate friend requests?
medium
A. Thread 1 sends request, Thread 2 waits until Thread 1 finishes, then sends request.
B. Thread 1 and Thread 2 both block on a lock, preventing duplicate requests.
C. Thread 1 creates request, Thread 2 reads request and cancels its own creation.
D. Thread 1 checks no request exists, context switch; Thread 2 checks no request exists, both create requests.

Solution

  1. Step 1: Identify the check-then-act race condition

    Both threads check for existing request before creation; without synchronization, both see none.
  2. Step 2: Trace thread interleaving

    Thread 1 checks and pauses before creating; Thread 2 checks and proceeds; both create duplicate requests.
  3. Final Answer:

    Option D -> Option D
  4. Quick Check:

    Unsynchronized check-then-act -> duplicate friend requests [check]
Hint: Check-then-act without lock -> race condition [check]
Common Mistakes:
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