Practice
Solution
Step 1: Understand ISP's goal
ISP aims to prevent clients from depending on methods they don't use, avoiding fat interfaces that force unnecessary implementations.Step 2: Analyze options
When clients depend on interfaces that contain methods they do not use, causing unnecessary implementation burden. correctly identifies the scenario where ISP helps by splitting fat interfaces. When all clients require the exact same set of methods, so a single fat interface simplifies design. describes a scenario where ISP is less needed. When you want to enforce a strict inheritance hierarchy with minimal interfaces. confuses inheritance hierarchy with interface segregation. When you want to reduce the number of interfaces to simplify the codebase. incorrectly assumes fewer interfaces always simplify design, ignoring interface misuse.Final Answer:
Option A -> Option AQuick Check:
ISP is about splitting interfaces to avoid forcing clients to implement unused methods.
- Believing fewer interfaces always mean better design
- Thinking ISP applies when all clients use all methods
- Confusing ISP with inheritance hierarchy rules
import copy
class Profile:
def __init__(self, name, scores):
self.name = name
self.scores = scores
def __deepcopy__(self, memo):
new_name = copy.deepcopy(self.name, memo)
new_scores = copy.deepcopy(self.scores, memo)
return Profile(new_name, new_scores)
original = Profile('Alice', [10, 20])
copy_obj = copy.deepcopy(original)
print('Original scores:', original.scores)
print('Copy scores:', copy_obj.scores)
copy_obj.scores.append(30)
print('After modifying copy scores:')
print('Original scores:', original.scores)
print('Copy scores:', copy_obj.scores)
Solution
Step 1: Trace initial print statements
Both original.scores and copy_obj.scores start as [10, 20], so first two prints show identical lists.Step 2: Trace modification and final prints
copy_obj.scores.append(30) modifies only the copy's scores list because deep copy created a new list. Original remains [10, 20]. Final prints reflect this separation.Final Answer:
Option B -> Option BQuick Check:
Deep copy prevents shared nested list -> original unchanged [OK]
- Assuming append affects original due to shared reference
Solution
Step 1: Trace instance creation
First call to Singleton() triggers __new__, _instance is None, so a new instance is created and assigned to _instance.Step 2: Trace second instance creation
Second call to Singleton() triggers __new__, _instance is not None, so the existing instance is returned.Final Answer:
Option C -> Option CQuick Check:
Both variables point to the same instance, so 'is' returns true [OK]
- Assuming new instance is created each time
- Confusing 'is' with '=='
- Expecting null or error due to lazy init
Solution
Step 1: Analyze cost() method
cost() calls self.cost() recursively, causing infinite recursion instead of delegating to wrapped object.Step 2: Identify correct delegation
Should call super().cost() to delegate to wrapped coffee object's cost method.Final Answer:
Option A -> Option AQuick Check:
Recursive call causes stack overflow, breaking decorator chain [OK]
- Calling self.cost() instead of super().cost() in decorators
- Forgetting to delegate calls properly
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 DQuick Check:
Extract and isolate responsibilities even if it requires effort -> SRP compliance.
- Avoiding refactoring due to perceived risk.
- Merging unrelated concerns for convenience.
- Ignoring SRP in legacy code.
