Bird
Raised Fist0
Interview Prepoop-design-patternsmediumAmazonGoogleMicrosoftFlipkartRazorpayZepto

Adapter vs Facade vs Proxy - Structural Pattern Comparison

Choose your preparation mode3 modes available

Start learning this pattern below

Jump into concepts and practice - no test required

or
Recommended
Test this pattern10 questions across easy, medium, and hard to know if this pattern is strong
Steps
setup

Create PaymentGateway instance

We instantiate the PaymentGateway class which provides the make_payment method to process payments.

💡 This class represents the existing interface that clients want to use but may not be compatible with their expected interface.
Line:gateway = PaymentGateway()
💡 Understanding the original class's interface is essential before adapting or wrapping it.
📊
Adapter vs Facade vs Proxy - Structural Pattern Comparison - Watch the Algorithm Execute, Step by Step
Watching each pattern's mechanism unfold visually helps you grasp their distinct roles and how they simplify or control interactions in complex systems.
Step 1/10
·Active fillAnswer cell
Setup of the adaptee class which the Adapter will wrap.
PaymentGateway
+make_payment(amount)()
Adapter pattern composition: Adapter holds a reference to adaptee.
PaymentGateway
+make_payment(amount)()
PaymentAdapter
gateway: PaymentGateway
+pay(amount)()
PaymentAdapter PaymentGateway
Adapter delegates method call to adaptee with interface translation.
PaymentGateway
+make_payment(amount)()
PaymentAdapter
gateway: PaymentGateway
+pay(amount)()
PaymentAdapter PaymentGateway
Subsystem classes with distinct responsibilities.
LightingSystem
+turn_on()()
+turn_off()()
SecuritySystem
+arm()()
+disarm()()
Facade pattern composition: Facade aggregates subsystems.
LightingSystem
+turn_on()()
+turn_off()()
SecuritySystem
+arm()()
+disarm()()
HomeFacade
lighting: LightingSystem
security: SecuritySystem
+leave_home()()
+arrive_home()()
HomeFacade LightingSystemHomeFacade SecuritySystem
Facade method triggers multiple subsystem operations.
LightingSystem
+turn_off()()
SecuritySystem
+arm()()
HomeFacade
lighting: LightingSystem
security: SecuritySystem
+leave_home()()
HomeFacade LightingSystemHomeFacade SecuritySystem
Real subject class for Proxy pattern.
Database
+query()()
Proxy pattern composition: Proxy holds reference to real subject and adds control.
Database
+query()()
DatabaseProxy
db: Database
authenticated: bool
+authenticate(user)()
+query()()
DatabaseProxy Database
Proxy updates internal state to allow access.
DatabaseProxy
authenticated: bool
+authenticate(user)()
Proxy forwards method call to real subject after access check.
DatabaseProxy
authenticated: bool
+query()()
Database
+query()()
DatabaseProxy Database

Key Takeaways

Adapter pattern changes the interface of an existing class to make it compatible with clients.

This insight is hard to see from code alone because the adapter's role is subtle and involves delegation with interface translation.

Facade pattern simplifies complex subsystem interactions by providing a unified interface.

Seeing the facade coordinate multiple subsystem calls visually clarifies how it reduces client complexity.

Proxy pattern controls access to a real subject by adding authentication or other checks before forwarding requests.

The proxy's control logic is often invisible in code but becomes clear when watching the access decision and delegation steps.

Practice

(1/5)
1. You are designing a system where multiple unrelated classes must guarantee implementation of certain methods but share no common code. Which abstraction mechanism is most appropriate to enforce this contract?
easy
A. Use an abstract class to define the methods and provide partial implementation.
B. Use an abstract class only if all classes share a common ancestor.
C. Use a concrete class and rely on inheritance for code reuse.
D. Use an interface to declare the methods without any implementation.

Solution

  1. Step 1: Identify the need for a contract without shared code

    Interfaces define method signatures without implementation, perfect for unrelated classes needing a common contract.
  2. Step 2: Why abstract classes are less suitable here

    Abstract classes provide partial implementation and require a common ancestor, which unrelated classes lack.
  3. Step 3: Why concrete classes and inheritance don't fit

    Concrete classes imply implementation and inheritance assumes a hierarchy, which is not guaranteed.
  4. Final Answer:

    Option D -> Option D
  5. Quick Check:

    Interfaces enforce contracts without imposing inheritance or shared code.
Hint: Use interfaces when only a contract is needed, abstract classes when sharing code.
Common Mistakes:
  • Assuming abstract classes are always better for abstraction.
  • Confusing contract enforcement with code reuse.
  • Believing unrelated classes can share an abstract class.
2. In the object-oriented design of a Snake and Ladder game, which component is primarily responsible for managing the state transitions of a player's position after a dice roll?
easy
A. The Dice class, since it generates the number that determines movement
B. The Player class, as it holds the current position and updates it directly
C. The Board class, because it contains the snakes and ladders and applies their effects
D. The GameController class, which orchestrates the game flow and updates player positions accordingly

Solution

  1. Step 1: Understand the role of Dice

    The Dice only generates a random number; it does not manage state transitions.
  2. Step 2: Consider Player class responsibilities

    Player holds position but should not decide how to update it considering snakes or ladders.
  3. Step 3: Analyze Board class role

    Board knows snakes and ladders but does not manage player state transitions directly.
  4. Step 4: Role of GameController

    GameController coordinates dice roll, queries Board for snakes/ladders, and updates Player position accordingly.
  5. Final Answer:

    Option D -> Option D
  6. Quick Check:

    GameController centralizes state transitions, ensuring separation of concerns.
Hint: GameController orchestrates state changes, not Dice or Player alone [OK]
Common Mistakes:
  • Thinking Dice manages player position
  • Assuming Player updates position without Board's input
  • Believing Board directly changes player state
3. Given the following code snippet, what will be printed when executing the last two lines?
from abc import ABC, abstractmethod

class PaymentStrategy(ABC):
    @abstractmethod
    def pay(self, amount):
        pass

class CreditCardStrategy(PaymentStrategy):
    def pay(self, amount):
        print(f"Processing credit card payment of ${amount}")

class UPIStrategy(PaymentStrategy):
    def pay(self, amount):
        print(f"Processing UPI payment of ${amount}")

class PaymentStrategyFactory:
    @staticmethod
    def get_strategy(method):
        if method == 'CreditCard':
            return CreditCardStrategy()
        elif method == 'UPI':
            return UPIStrategy()
        else:
            raise ValueError('Invalid payment method')

class PaymentProcessor:
    def __init__(self, strategy: PaymentStrategy):
        self.strategy = strategy

    def pay(self, amount):
        self.strategy.pay(amount)

processor = PaymentProcessor(PaymentStrategyFactory.get_strategy('UPI'))
processor.pay(100)
easy
A. Raises ValueError: Invalid payment method
B. Processing credit card payment of $100
C. Processing net banking payment of $100
D. Processing UPI payment of $100

Solution

  1. Step 1: Trace strategy selection

    The factory method get_strategy('UPI') returns an instance of UPIStrategy.
  2. Step 2: Trace payment method call

    The pay method of UPIStrategy prints "Processing UPI payment of $100".
  3. Final Answer:

    Option D -> Option D
  4. Quick Check:

    Correct strategy instance leads to correct output [OK]
Hint: Factory returns correct strategy instance for method [OK]
Common Mistakes:
  • Confusing strategy returned or output string
4. Identify the bug in the following Python code implementing deep copy for the Profile class:
import copy

class Profile:
    def __init__(self, name, scores):
        self.name = name
        self.scores = scores

    def __deepcopy__(self, memo):
        new_name = self.name  # Bug here
        new_scores = copy.deepcopy(self.scores, memo)
        return Profile(new_name, new_scores)

original = Profile('Alice', [10, 20])
copy_obj = copy.deepcopy(original)
copy_obj.scores.append(30)
print(original.scores)
medium
A. Line assigning new_name = self.name does not deepcopy the name string
B. Line assigning new_scores = copy.deepcopy(self.scores, memo) incorrectly copies scores
C. The __init__ method does not initialize scores properly
D. The return statement returns a new Profile instead of modifying self

Solution

  1. Step 1: Examine __deepcopy__ method

    The line new_name = self.name copies the reference to the name string instead of deep copying it.
  2. Step 2: Understand impact

    Strings are immutable in Python, so shallow copy is usually safe, but if name were a mutable object, this would cause shared references and bugs. Proper deep copy should be used for consistency.
  3. Final Answer:

    Option A -> Option A
  4. Quick Check:

    Only new_name assignment lacks deepcopy -> subtle bug [OK]
Hint: All nested fields must be deep copied consistently [OK]
Common Mistakes:
  • Forgetting to deepcopy all nested fields, assuming immutables are safe
5. If the Library Management System must support reserving books that are currently loaned out, which design change best handles the edge case where multiple users reserve the same book simultaneously?
hard
A. Let the User class maintain their own reservation list independently without central coordination.
B. Allow multiple reservations by updating the book's status to 'reserved' without tracking order.
C. Add a reservation queue in the LoanManager and process reservations in FIFO order with atomic updates.
D. Implement a polling mechanism where users repeatedly check book availability without reservations.

Solution

  1. Step 1: Why a reservation queue?

    FIFO queue ensures fairness and order in handling multiple reservations.
  2. Step 2: Why not multiple reservations without order?

    Allow multiple reservations by updating the book's status to 'reserved' without tracking order. causes ambiguity and race conditions in fulfilling reservations.
  3. Step 3: Why not user-maintained reservations?

    Let the User class maintain their own reservation list independently without central coordination. lacks centralized control, risking inconsistent reservation states.
  4. Step 4: Why not polling without reservations?

    Implement a polling mechanism where users repeatedly check book availability without reservations. leads to poor user experience and race conditions.
  5. Final Answer:

    Option C -> Option C
  6. Quick Check:

    Centralized, ordered reservation management with atomic updates handles concurrency and fairness.
Hint: Use a centralized FIFO reservation queue to handle concurrent reservations fairly [OK]
Common Mistakes:
  • Ignoring reservation order and fairness
  • Decentralizing reservation tracking
  • Relying on user polling instead of system-managed reservations