💡 The context can switch strategies dynamically, demonstrating the pattern's flexibility.
traverse
Invoke pay() on PaymentProcessor with $50 using UPIStrategy
Calling processor.pay(50) now delegates to the UPIStrategy's pay method, processing a UPI payment.
💡 The processor uses the updated strategy to perform payment without any conditional logic.
Line:processor.pay(50)
💡 The strategy pattern allows seamless switching and execution of different behaviors at runtime.
prune
Summary: Polymorphism replaces conditionals
The system uses polymorphism to select and execute payment behavior dynamically, eliminating the need for conditional statements in the payment processing logic.
💡 This step highlights the core benefit of the strategy pattern: flexible behavior changes without modifying client code.
💡 Polymorphism enables interchangeable strategies, improving code maintainability and extensibility.
reconstruct
Final State: Strategy Pattern in Action
The final state shows all classes and their relationships, with the PaymentProcessor holding a reference to the current strategy. The system is ready to process payments flexibly without conditionals.
💡 This final view consolidates your understanding of the pattern's structure and runtime behavior.
💡 The design enables easy extension by adding new strategies without changing existing code.
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
Step 1: Check availability atomically
LoanManager must verify the book is available before granting loan to prevent race conditions.
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.
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.
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.
Final Answer:
Option D -> Option D
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
Step 1: Dice roll triggers GameController
Dice generates number; GameController uses it to calculate tentative position.
Step 2: Board checks for ladder or snake
GameController queries Board to see if new position has ladder or snake.
Step 3: GameController updates Player position
Based on Board's info, GameController updates Player's position accordingly.
Step 4: Player position is finalized
Player's position reflects ladder climb if applicable.
Final Answer:
Option A -> Option A
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
Step 1: Understand LSP's core requirement
LSP requires that subclasses can replace their superclasses without altering desirable properties of the program, especially behavior.
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.
Final Answer:
Option D -> Option D
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
Step 1: Understand polymorphic dispatch
Method calls on subclass instances invoke the subclass's overridden method directly.
Step 2: Base class code remains unchanged
The base class is closed for modification; no changes are made to call subclass methods explicitly.
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.
Final Answer:
Option C -> Option C
Quick Check:
Polymorphism enables extension without modifying base code.
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
Step 1: Recall LSP precondition rule
Subclasses must not strengthen preconditions; they can only maintain or weaken them.
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.
Final Answer:
Option A -> Option A
Quick Check:
Strengthening preconditions violates LSP.
Hint: Preconditions can only be weakened, not strengthened, in subclasses.