Practice
Solution
Step 1: Identify the need for a contract without shared code
Interfaces define method signatures without implementation, perfect for unrelated classes needing a common contract.Step 2: Why abstract classes are less suitable here
Abstract classes provide partial implementation and require a common ancestor, which unrelated classes lack.Step 3: Why concrete classes and inheritance don't fit
Concrete classes imply implementation and inheritance assumes a hierarchy, which is not guaranteed.Final Answer:
Option D -> Option DQuick Check:
Interfaces enforce contracts without imposing inheritance or shared code.
- Assuming abstract classes are always better for abstraction.
- Confusing contract enforcement with code reuse.
- Believing unrelated classes can share an abstract class.
Solution
Step 1: Understand delegation in composition
In composition, the main object delegates responsibilities to composed behavior objects rather than handling all logic itself.Step 2: Analyze each option
The object directly executes the action code inherited from its superclass describes inheritance, not composition. The object checks flags internally and runs conditional code for each behavior implies flag-based conditional logic, which is less flexible. The object creates a new subclass instance dynamically to handle the action is not a typical or practical approach.Step 3: Confirm correct flow
The composed behaviors receive the delegated call and execute their specific logic, enabling modular and maintainable design.Final Answer:
Option A -> Option AQuick Check:
Delegation to composed objects is the hallmark of composition-based design.
- Confusing inheritance method calls with composition delegation
- Assuming flags control behavior execution internally
Solution
Step 1: Recall LSP postcondition rule
Subclasses must not strengthen postconditions; they can only maintain or weaken them.Step 2: Trace client call
The client expects results conforming to the superclass contract. If subclass returns stricter results, some clients expecting broader results may fail.Step 3: Analyze options
The subclass method returns a stricter result than expected, potentially causing client failures. correctly identifies potential client failures due to stricter postconditions. The client receives a result that meets the superclass contract, so no issues arise. is false because stricter postconditions can break clients. The client silently ignores the stricter postcondition, so behavior is unaffected. is incorrect; clients cannot ignore contract violations silently. The subclass method throws an exception due to the strengthened postcondition. is not guaranteed; exceptions are not implied by postcondition strengthening.Final Answer:
Option B -> Option BQuick Check:
Strengthening postconditions risks breaking client expectations.
- Assuming stricter postconditions are safe
- Believing clients ignore contract violations
- Confusing exceptions with contract violations
Solution
Step 1: Identify trade-offs of OCP
Strict adherence often results in many small subclasses, increasing complexity.Step 2: Why other options are false
It forces all changes to be made in a single base class, increasing risk of bugs is opposite to OCP's goal; changes are made via extension, not base modification. It eliminates the need for interfaces or abstract classes, simplifying design is false because OCP relies on abstractions like interfaces. It guarantees zero runtime overhead due to polymorphism is incorrect; polymorphism can introduce slight runtime overhead.Final Answer:
Option B -> Option BQuick Check:
Class explosion is a known practical downside of OCP.
- Thinking OCP centralizes changes in base classes
- Believing OCP removes need for abstractions
- Assuming polymorphism has no runtime cost
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)
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 AQuick Check:
Only new_name assignment lacks deepcopy -> subtle bug [OK]
- Forgetting to deepcopy all nested fields, assuming immutables are safe
