Practice
Solution
Step 1: Identify reasons to change
User account management changes when user data or authentication changes; email notifications change when messaging or delivery requirements change.Step 2: Apply SRP
Since these reasons to change differ, they should be separated into different classes to avoid coupling unrelated changes.Step 3: Evaluate other options
Options A, C, and D combine responsibilities, increasing coupling and reducing cohesion, violating SRP.Final Answer:
Option A -> Option AQuick Check:
Separate classes for distinct reasons to change -> SRP compliant.
- Assuming related domain means same responsibility.
- Combining functionalities to reduce class count.
- Embedding multiple responsibilities for convenience.
Solution
Step 1: Interfaces and state
Interfaces cannot hold instance fields; they only declare constants (static final fields).Step 2: Abstract classes and state
Abstract classes can have constructors and instance variables to maintain state.Step 3: Default methods in interfaces
Modern languages allow default method implementations in interfaces.Step 4: Partial implementation in abstract classes
Abstract classes can provide some method implementations while leaving others abstract.Final Answer:
Option A -> Option AQuick Check:
Interfaces cannot hold mutable state; abstract classes can.
- Assuming interfaces can have instance variables.
- Confusing default methods with full implementations.
- Believing abstract classes cannot have constructors.
Solution
Step 1: Understand pessimistic locking
Pessimistic locking prevents concurrent conflicting access, so Using pessimistic locking on book records ensures no two users can borrow the same book simultaneously. is correct.Step 2: Why optimistic locking is not always deadlock?
Optimistic locking is unsuitable because it always leads to deadlocks in high contention scenarios. is incorrect; optimistic locking reduces deadlocks by retrying on conflicts.Step 3: Atomic operations prevent race conditions
Implementing atomic check-and-update operations prevents race conditions during loan processing. is correct and essential for concurrency safety.Step 4: Versioning aids optimistic concurrency
Using version numbers or timestamps can help detect conflicting updates in optimistic concurrency control. correctly describes conflict detection mechanisms.Final Answer:
Option A -> Option AQuick Check:
Optimistic locking reduces deadlocks; it does not always cause them.
- Believing optimistic locking always causes deadlocks
- Confusing locking types and their effects
- Ignoring atomicity in concurrency control
Solution
Step 1: Understand the Diamond Problem
Diamond Problem arises when a class inherits from two classes that share a common ancestor, causing ambiguity.Step 2: Evaluate Multiple inheritance always leads to ambiguous method calls that cannot be resolved.
Multiple inheritance can cause ambiguity, but languages use MRO to resolve it, so it is not always unresolved.Step 3: Evaluate Multiple inheritance eliminates the need for Method Resolution Order (MRO).
MRO is essential in multiple inheritance to resolve method calls, so multiple inheritance does not eliminate MRO.Step 4: Evaluate Multiple inheritance reduces code reuse compared to single inheritance.
Multiple inheritance generally increases code reuse by combining features from multiple classes.Step 5: Correct trade-off
Using multiple inheritance can increase complexity and make the class hierarchy harder to understand and maintain. correctly identifies that multiple inheritance increases complexity and can make hierarchies harder to maintain.Final Answer:
Option D -> Option DQuick Check:
Complexity and maintainability are key trade-offs in multiple inheritance.
- Believing multiple inheritance always causes irresolvable ambiguity
- Thinking MRO is unnecessary with multiple inheritance
- Assuming multiple inheritance reduces code reuse
Solution
Step 1: Understand runtime strategy switching
Switching strategies at runtime requires decoupling strategy selection from the context and handling invalid inputs gracefully.Step 2: Identify design that supports clean extensibility and error handling
Using a factory method to create strategy instances and injecting them into PaymentProcessor allows runtime flexibility and clean error handling via exceptions.Final Answer:
Option A -> Option AQuick Check:
Factory + DI + exceptions enable runtime switching and robustness [OK]
- Hardcoding strategies or using conditionals inside context
