✓ The Open/Closed Principle is implemented by defining an abstract base class and extending it with new subclasses without modifying existing code.
This insight is hard to see from code alone because the principle is about design intent and extensibility, which is clearer when visualized step-by-step.
✓ Client code depends on abstractions (the Shape interface) rather than concrete implementations, enabling polymorphism.
Seeing the client class interact only with the abstract class clarifies how extension is possible without modification.
✓ Adding new functionality (like the Triangle class) requires no changes to existing classes or client code, demonstrating maintainability.
The visualization shows how new subclasses integrate seamlessly, which is difficult to grasp by reading code alone.
Practice
(1/5)
1. When a class inherits from multiple classes that have a method with the same name, describe the step-by-step process the Method Resolution Order (MRO) uses to determine which method is called.
easy
A. MRO uses a linearization algorithm that merges the order of parents and their ancestors to find the method.
B. MRO searches the first parent class fully before moving to the next parent class.
C. MRO always calls the method from the last parent class listed in the inheritance.
D. MRO randomly picks the method from any parent class that defines it.
Solution
Step 1: Understand naive search
MRO does not simply search the first parent class fully before moving to the next; it uses a more sophisticated approach.
Step 2: Recognize MRO linearization
MRO uses a specific linearization (like C3 linearization) that merges parent classes and their ancestors in a consistent order.
Step 3: Eliminate incorrect options
MRO always calls the method from the last parent class listed in the inheritance is incorrect because the last parent is not always chosen; order and ancestors matter. MRO randomly picks the method from any parent class that defines it is incorrect because MRO is deterministic, not random.
Final Answer:
Option A -> Option A
Quick Check:
MRO merges inheritance hierarchies to find the correct method in a predictable order.
Hint: MRO = deterministic linearization of inheritance graph
Common Mistakes:
Assuming simple left-to-right search suffices
Believing last parent always overrides
Thinking method choice is random
2. You need to traverse a tree-like structure where each node can be either a single element or a composite containing multiple child elements. The traversal should allow clients to iterate over all elements uniformly without exposing the internal structure. Which design approach best fits this requirement?
easy
A. Implement the Composite pattern combined with an Iterator interface to provide a unified traversal abstraction.
B. Use a recursive brute force traversal that manually visits each node and its children.
C. Apply a greedy algorithm that visits nodes based on a heuristic to minimize traversal time.
D. Use dynamic programming to store and reuse traversal results for overlapping subtrees.
Solution
Step 1: Understand the problem structure
The problem involves a tree with nodes that can be leaves or composites containing children, requiring uniform traversal.
Step 2: Identify the suitable pattern
The Composite pattern allows treating individual objects and compositions uniformly, and combining it with an Iterator abstracts traversal details.
Hint: Composite + Iterator unifies traversal of tree structures [OK]
Common Mistakes:
Confusing traversal with greedy or DP approaches
Using manual recursion without abstraction
3. Examine the following buggy Invoker code snippet implementing undo/redo stacks. Which line contains the subtle bug that can cause incorrect redo behavior after executing a new command?
medium
A. Line where redo_stack is not cleared after new command execution
B. Line where redo_stack is appended in undo
C. Line where command.execute() is called in execute_command
D. Line where undo_stack is popped in undo
Solution
Step 1: Identify expected redo stack behavior
After executing a new command, redo stack must be cleared to avoid invalid redo commands.
Step 2: Locate missing redo_stack.clear()
The execute_command method lacks clearing redo_stack, so redo commands remain stale.
Final Answer:
Option A -> Option A
Quick Check:
Redo stack not cleared after new command breaks redo correctness [OK]
Hint: Redo stack must clear on new command execution [OK]
Common Mistakes:
Forgetting to clear redo stack
Undoing commands in wrong order
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
Step 1: Examine __deepcopy__ method
The line new_name = self.name copies the reference to the name string instead of deep copying it.
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.
Final Answer:
Option A -> Option A
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. Identify the bug in the following code snippet implementing the strategy pattern for payment processing:
class PaymentProcessor:
def __init__(self, strategy: PaymentStrategy):
self.strategy = strategy
def pay(self, amount):
if isinstance(self.strategy, CreditCardStrategy):
print(f"Processing credit card payment of ${amount}")
elif isinstance(self.strategy, UPIStrategy):
print(f"Processing UPI payment of ${amount}")
else:
print("Invalid payment method")
medium
A. The bug is that the pay method uses conditional checks instead of polymorphism.
B. The bug is missing a call to the strategy's pay method inside pay().
C. The bug is in the constructor where the strategy is not assigned properly.
D. The bug is that the else clause should raise an exception instead of printing.
Solution
Step 1: Analyze the pay method implementation
The pay method uses isinstance checks and conditionals to decide behavior, which defeats the purpose of polymorphism.
Step 2: Identify correct polymorphic usage
The correct approach is to call self.strategy.pay(amount) directly, letting each strategy handle its own payment logic.
Final Answer:
Option A -> Option A
Quick Check:
Using conditionals in pay() breaks the strategy pattern [OK]
Hint: Strategy pattern requires polymorphic calls, not conditionals [OK]
Common Mistakes:
Using type checks instead of polymorphism
Recommended
Ready to prove it?
Pass the test and this pattern moves unknownstrongwhile earning +1 Grip toward your certificate.