Bird
Raised Fist0
Interview Prepoop-design-patternsmediumAmazonGoogleMicrosoftFlipkartRazorpaySwiggyPhonePe

Strategy Pattern - Replace Conditionals with Polymorphism

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

Define PaymentStrategy Interface

The abstract PaymentStrategy interface is defined with an abstract method pay(amount). This sets the contract for all payment strategies.

💡 Defining an interface ensures all payment methods implement the same method, enabling polymorphism.
Line:class PaymentStrategy(ABC): @abstractmethod def pay(self, amount): pass
💡 All payment strategies will share a common interface, allowing interchangeable use.
📊
Strategy Pattern - Replace Conditionals with Polymorphism - Watch the Algorithm Execute, Step by Step
Watching each step reveals how polymorphism replaces conditional logic, making the code more flexible and extensible.
Step 1/10
·Active fillAnswer cell
Defines the Strategy interface with an abstract method for payment.
«abstract»PaymentStrategy
+pay()
Concrete strategy implements the Strategy interface.
«abstract»PaymentStrategy
+pay()
CreditCardStrategy
+pay()
CreditCardStrategy PaymentStrategy
Another concrete strategy implementing the Strategy interface.
«abstract»PaymentStrategy
+pay()
CreditCardStrategy
+pay()
UPIStrategy
+pay()
CreditCardStrategy PaymentStrategyUPIStrategy PaymentStrategy
Factory pattern encapsulates strategy selection logic.
«abstract»PaymentStrategy
+pay()
CreditCardStrategy
+pay()
UPIStrategy
+pay()
PaymentStrategyFactory
+get_strategy()
CreditCardStrategy PaymentStrategyUPIStrategy PaymentStrategy
Dependency injection of strategy into context class.
«abstract»PaymentStrategy
+pay()
CreditCardStrategy
+pay()
UPIStrategy
+pay()
PaymentStrategyFactory
+get_strategy()
PaymentProcessor
strategy: PaymentStrategy
+__init__()
+set_strategy()
+pay()
CreditCardStrategy PaymentStrategyUPIStrategy PaymentStrategyPaymentProcessor PaymentStrategy (1:1)
Context delegates behavior to current strategy.
«abstract»PaymentStrategy
+pay()
CreditCardStrategy
+pay()
UPIStrategy
+pay()
PaymentStrategyFactory
+get_strategy()
PaymentProcessor
strategy: PaymentStrategy
+__init__()
+set_strategy()
+pay()
CreditCardStrategy PaymentStrategyUPIStrategy PaymentStrategyPaymentProcessor PaymentStrategy (1:1)
Context updates its strategy reference to a new concrete strategy.
«abstract»PaymentStrategy
+pay()
CreditCardStrategy
+pay()
UPIStrategy
+pay()
PaymentStrategyFactory
+get_strategy()
PaymentProcessor
strategy: PaymentStrategy
+__init__()
+set_strategy()
+pay()
CreditCardStrategy PaymentStrategyUPIStrategy PaymentStrategyPaymentProcessor PaymentStrategy (1:1)
Context delegates to the updated strategy's method.
«abstract»PaymentStrategy
+pay()
CreditCardStrategy
+pay()
UPIStrategy
+pay()
PaymentStrategyFactory
+get_strategy()
PaymentProcessor
strategy: PaymentStrategy
+__init__()
+set_strategy()
+pay()
CreditCardStrategy PaymentStrategyUPIStrategy PaymentStrategyPaymentProcessor PaymentStrategy (1:1)
Shows the overall design where polymorphism replaces conditionals.
«abstract»PaymentStrategy
+pay()
CreditCardStrategy
+pay()
UPIStrategy
+pay()
PaymentStrategyFactory
+get_strategy()
PaymentProcessor
strategy: PaymentStrategy
+__init__()
+set_strategy()
+pay()
CreditCardStrategy PaymentStrategyUPIStrategy PaymentStrategyPaymentProcessor PaymentStrategy (1:1)
Final consolidated design showing Strategy pattern structure and relationships.

Key Takeaways

Polymorphism replaces conditional logic by allowing interchangeable strategies.

Reading code alone hides how runtime behavior changes; watching the pattern in action clarifies this.

Dependency injection enables flexible strategy assignment and switching at runtime.

Seeing the strategy passed into the processor shows how behavior can be changed without modifying the processor.

The factory centralizes conditional logic, keeping client code clean and focused on polymorphism.

The trace reveals how the factory isolates conditionals, which might be overlooked in static code reading.

Practice

(1/5)
1. Trace the sequence of events when a user attempts to borrow a book that is currently checked out by another user in a concurrent environment. Which step correctly describes the system's behavior?
easy
A. The Library class queues the request and processes it after the current loan expires without immediate feedback
B. The system immediately updates the book status to 'borrowed' for the requesting user without checking current loans
C. The User class updates its borrowed books list first, then the system verifies availability asynchronously
D. The LoanManager checks the book's availability, detects it is loaned out, and denies the request atomically

Solution

  1. Step 1: Check availability atomically

    LoanManager must verify the book is available before granting loan to prevent race conditions.
  2. Step 2: Why not immediate update?

    The system immediately updates the book status to 'borrowed' for the requesting user without checking current loans risks data inconsistency by ignoring current loan state.
  3. Step 3: Why not User updates first?

    The User class updates its borrowed books list first, then the system verifies availability asynchronously breaks transactional integrity and can cause stale or conflicting states.
  4. Step 4: Why not queue without feedback?

    The Library class queues the request and processes it after the current loan expires without immediate feedback delays user feedback and complicates user experience; immediate denial is standard.
  5. Final Answer:

    Option D -> Option D
  6. Quick Check:

    Atomic availability check and denial maintain consistency and user clarity.
Hint: Always verify resource availability atomically before state changes in concurrency [OK]
Common Mistakes:
  • Assuming immediate state update without checks
  • Updating user state before system validation
  • Delaying feedback by queuing without immediate response
2. Trace the sequence of events when a player rolls the dice and lands on a ladder in a well-designed Snake and Ladder game. Which of the following best describes the correct order of internal state changes?
easy
A. Player rolls dice -> GameController calculates new position -> Board checks for ladder -> GameController updates Player position accordingly
B. Player rolls dice -> Player position updated -> Board checks for ladder -> Player position updated again if ladder found
C. Player rolls dice -> Board updates Player position directly if ladder found -> Player notified of new position
D. Player rolls dice -> Dice notifies Board -> Board updates Player position -> GameController confirms move

Solution

  1. Step 1: Dice roll triggers GameController

    Dice generates number; GameController uses it to calculate tentative position.
  2. Step 2: Board checks for ladder or snake

    GameController queries Board to see if new position has ladder or snake.
  3. Step 3: GameController updates Player position

    Based on Board's info, GameController updates Player's position accordingly.
  4. Step 4: Player position is finalized

    Player's position reflects ladder climb if applicable.
  5. Final Answer:

    Option A -> Option A
  6. Quick Check:

    Only GameController manages state changes; Board is queried but does not update Player directly.
Hint: GameController mediates all state changes after dice roll [OK]
Common Mistakes:
  • Assuming Player updates position before Board check
  • Believing Board directly updates Player position
  • Thinking Dice notifies Board
3. In which scenario is applying the Liskov Substitution Principle (LSP) most critical to ensure system correctness?
easy
A. When a subclass adds new methods without overriding any superclass methods.
B. When a subclass uses composition instead of inheritance.
C. When a subclass narrows the input parameter types of an overridden method.
D. When a subclass extends a superclass but changes the expected behavior of inherited methods.

Solution

  1. Step 1: Understand LSP's core requirement

    LSP requires that subclasses can replace their superclasses without altering desirable properties of the program, especially behavior.
  2. Step 2: Analyze each option carefully

    When a subclass extends a superclass but changes the expected behavior of inherited methods. describes a subclass changing expected behavior, which violates LSP. When a subclass adds new methods without overriding any superclass methods. is safe as adding methods doesn't break substitutability. When a subclass narrows the input parameter types of an overridden method. narrows input types (contravariance violation), which breaks substitutability but is less direct than changing behavior. When a subclass uses composition instead of inheritance. is unrelated to LSP since composition is an alternative to inheritance.
  3. Final Answer:

    Option D -> Option D
  4. Quick Check:

    Only changing inherited method behavior breaks LSP directly.
Hint: LSP is about preserving inherited behavior, not just adding features.
Common Mistakes:
  • Thinking adding methods breaks LSP
  • Confusing covariance and contravariance in parameters
  • Assuming composition relates directly to LSP
4. When a new feature is added by extending a class hierarchy following the Open/Closed Principle, what is the typical sequence of events when the system executes a method call on the new subclass instance?
easy
A. The base class method is always executed first, then the subclass method overrides it afterward
B. The system duplicates the base class code inside the subclass to avoid modifying the base
C. The subclass method is invoked directly due to polymorphism, without modifying base class code
D. Both base and subclass methods execute sequentially because the base class is modified to call the subclass

Solution

  1. Step 1: Understand polymorphic dispatch

    Method calls on subclass instances invoke the subclass's overridden method directly.
  2. Step 2: Base class code remains unchanged

    The base class is closed for modification; no changes are made to call subclass methods explicitly.
  3. Step 3: Why other options are incorrect

    The base class method is always executed first, then the subclass method overrides it afterward incorrectly suggests base method runs first then subclass; Both base and subclass methods execute sequentially because the base class is modified to call the subclass implies base class modification; The system duplicates the base class code inside the subclass to avoid modifying the base suggests code duplication, violating DRY and OCP.
  4. Final Answer:

    Option C -> Option C
  5. Quick Check:

    Polymorphism enables extension without modifying base code.
Hint: Polymorphism calls subclass methods directly [OK]
Common Mistakes:
  • Assuming base method always runs first
  • Believing base class must be modified to support extension
  • Thinking code duplication is a valid OCP strategy
5. Which of the following statements about the Liskov Substitution Principle is INCORRECT?
medium
A. A subclass can strengthen preconditions of an inherited method to ensure better input validation.
B. A subclass must not weaken postconditions of an inherited method.
C. Covariance in return types is allowed under LSP.
D. Contravariance in method parameter types is allowed under LSP.

Solution

  1. Step 1: Recall LSP precondition rule

    Subclasses must not strengthen preconditions; they can only maintain or weaken them.
  2. Step 2: Analyze each statement

    A subclass can strengthen preconditions of an inherited method to ensure better input validation. is incorrect because strengthening preconditions breaks substitutability. A subclass must not weaken postconditions of an inherited method. is correct; subclasses can weaken postconditions. Covariance in return types is allowed under LSP. is correct; covariance in return types is allowed. Contravariance in method parameter types is allowed under LSP. is correct; contravariance in parameter types is allowed.
  3. Final Answer:

    Option A -> Option A
  4. Quick Check:

    Strengthening preconditions violates LSP.
Hint: Preconditions can only be weakened, not strengthened, in subclasses.
Common Mistakes:
  • Confusing precondition and postcondition rules
  • Believing strengthening preconditions is safe
  • Misunderstanding covariance and contravariance