Practice
Solution
Step 1: Understand the requirement for families of related objects
The problem states the need to create related vehicles (e.g., electric car and electric bike) that belong to a family and must be consistent.Step 2: Match pattern to requirement
Abstract Factory is designed to create families of related objects, ensuring that the created objects are compatible and consistent.Final Answer:
Option B -> Option BQuick Check:
Abstract Factory creates related object families, Factory creates single objects [OK]
- Confusing Factory with Abstract Factory
- Using Builder for simple object creation
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
from abc import ABC, abstractmethod
class PaymentStrategy(ABC):
@abstractmethod
def pay(self, amount):
pass
class CreditCardStrategy(PaymentStrategy):
def pay(self, amount):
print(f"Processing credit card payment of ${amount}")
class UPIStrategy(PaymentStrategy):
def pay(self, amount):
print(f"Processing UPI payment of ${amount}")
class PaymentStrategyFactory:
@staticmethod
def get_strategy(method):
if method == 'CreditCard':
return CreditCardStrategy()
elif method == 'UPI':
return UPIStrategy()
else:
raise ValueError('Invalid payment method')
class PaymentProcessor:
def __init__(self, strategy: PaymentStrategy):
self.strategy = strategy
def pay(self, amount):
self.strategy.pay(amount)
processor = PaymentProcessor(PaymentStrategyFactory.get_strategy('UPI'))
processor.pay(100)
Solution
Step 1: Trace strategy selection
The factory methodget_strategy('UPI')returns an instance ofUPIStrategy.Step 2: Trace payment method call
Thepaymethod ofUPIStrategyprints "Processing UPI payment of $100".Final Answer:
Option D -> Option DQuick Check:
Correct strategy instance leads to correct output [OK]
- Confusing strategy returned or output string
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
Solution
Step 1: Review overriding rules
Overriding requires the same method signature and enables runtime polymorphism.Step 2: Analyze each statement
Overriding methods must have the same method signature as the method in the superclass is correct; signatures must match. Overriding enables runtime polymorphism through dynamic dispatch is correct; overriding uses dynamic dispatch. A subclass can call the superclass's overridden method using a special keyword is correct; subclasses can call superclass methods (e.g., via super keyword).Step 3: Identify incorrect statement
Overriding is resolved at compile time by the compiler is incorrect because overriding is resolved at runtime, not compile time.Final Answer:
Option D -> Option DQuick Check:
Overriding is a runtime mechanism, not compile-time.
- Believing overriding is compile-time resolved
- Confusing signature requirements with overloading
- Ignoring ability to call superclass methods
