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 Base Class Shape
We define the abstract base class Shape with methods to set width and height and to calculate area. This sets the contract that all shapes must follow.
💡 Defining the base class establishes the expected interface and behavior that subtypes must honor.
⚠ LSP violation: Square does not behave as Rectangle
Key Takeaways
✓ Subtypes must preserve the behavioral contract of their base types to satisfy the Liskov Substitution Principle.
This is hard to see from code alone because method signatures may match but behavior can differ subtly.
✓ Violating the contract leads to unexpected results when subtypes are used polymorphically.
Visualizing the substitution and its effect on state and output clarifies why this is problematic.
✓ Designing subtypes requires careful attention to not only interface but also semantics and side effects.
The trace shows concretely how Square's setters break expectations, a subtle but critical insight.
Practice
(1/5)
1. You have a legacy payment processing system with an incompatible interface, and you want to integrate it into a new e-commerce platform without changing the legacy code. Which pattern best suits this scenario?
easy
A. Facade, to provide a simplified interface to the legacy system
B. Adapter, to convert the legacy interface to the new platform's expected interface
C. Proxy, to control access and add security to the legacy system
D. Decorator, to add new behavior to the legacy system dynamically
Solution
Step 1: Identify the problem
The legacy system's interface is incompatible with the new platform.
Step 2: Understand pattern intents
Adapter converts one interface to another, enabling integration without changing legacy code. Facade simplifies a complex subsystem but doesn't change interfaces. Proxy controls access, not interface compatibility. Decorator adds behavior dynamically, unrelated to interface mismatch.
Final Answer:
Option B -> Option B
Quick Check:
Adapter is the go-to pattern for interface incompatibility issues.
2. Given the following Python code using the Decorator Pattern, what is the output of print(coffee.description()) and print(coffee.cost()) after wrapping a SimpleCoffee with MilkDecorator (amount=2) and then SugarDecorator (amount=1)?
easy
A. Coffee, Milk(2), Sugar(1)
6.3
B. Coffee, Sugar(1), Milk(2)
5.8
C. Coffee, Sugar(1), Milk(2)
6.3
D. Coffee, Milk(2), Sugar(1)
5.8
Solution
Step 1: Trace description calls
Starting from SugarDecorator: description() calls MilkDecorator.description(), which calls SimpleCoffee.description() returning "Coffee". Then MilkDecorator adds ", Milk(2)", SugarDecorator adds ", Sugar(1)" -> "Coffee, Milk(2), Sugar(1)".
Description order matches wrapping order; cost sums correctly [OK]
Hint: Decorator calls chain in wrapping order [OK]
Common Mistakes:
Mixing order of decorators in description
Forgetting to multiply cost by amount
3. In designing a parking lot system using OOP, which component is best suited to decide which type of parking spot (e.g., compact, large, handicapped) should be allocated to an incoming vehicle?
easy
A. The ParkingLot class, as it manages all spots and vehicles
B. The ParkingSpot class, since it represents the spot's characteristics
C. The Vehicle class, because it knows its own size and type
D. A Factory or Strategy pattern component that encapsulates the allocation logic
Solution
Step 1: Understand responsibilities
The Vehicle class only knows about itself, not allocation rules. The ParkingSpot class represents a spot but doesn't decide allocation. The ParkingLot manages overall state but delegating allocation logic to a dedicated component improves modularity.
Step 2: Recognize design pattern role
The Factory or Strategy pattern encapsulates allocation logic, allowing easy extension and modification without changing core classes.
Final Answer:
Option D -> Option D
Quick Check:
Allocation logic centralized -> easier to maintain and extend [OK]
Hint: Allocation logic belongs in a dedicated pattern component, not core entities [OK]
Common Mistakes:
Assigning allocation responsibility to Vehicle or ParkingSpot classes
Putting all logic inside ParkingLot class leading to tight coupling
4. What is the time complexity of constructing a complex object using the Builder pattern when the object has k parts to build?
medium
A. O(k), because each of the k parts is built sequentially
B. O(1), since each build method is called once
C. O(k^2), due to nested calls between builder methods
D. O(log k), as parts are built using divide-and-conquer
Solution
Step 1: Identify number of build steps
The Builder pattern calls a build method for each part, so k parts mean k method calls.
Step 2: Analyze time per build call
Each build method adds a part in O(1) time, so total time is O(k).
Final Answer:
Option A -> Option A
Quick Check:
Linear time proportional to number of parts built [OK]
Hint: Each part built once -> O(k) time [OK]
Common Mistakes:
Confusing constant time with O(k)
Assuming nested calls cause O(k^2)
5. In a legacy system, a class handles both business logic and logging. You want to refactor it following SRP, but the logging code is tightly intertwined with business logic. What is the best approach to refactor this while respecting SRP?
hard
A. Ignore SRP in this case because legacy code should not be refactored.
B. Leave logging inside the class because extracting it would break existing functionality and increase risk.
C. Merge business logic and logging into a utility class to centralize all cross-cutting concerns.
D. Extract logging into a separate class and replace logging calls with calls to this new class, even if it requires modifying many places.
Solution
Step 1: Identify responsibilities
Business logic and logging are separate reasons to change.
Step 2: Refactor strategy
Extract logging into its own class to isolate changes related to logging.
Step 3: Address tight coupling
Though intertwined, refactoring calls to the new logging class improves maintainability and respects SRP.
Step 4: Evaluate other options
Options B and D avoid refactoring, risking future issues; C increases coupling by merging unrelated concerns.
Final Answer:
Option D -> Option D
Quick Check:
Extract and isolate responsibilities even if it requires effort -> SRP compliance.
Hint: Separate concerns even if intertwined; refactor stepwise.