Bird
Raised Fist0

When to Use Inheritance vs Composition - The Rule Every Senior Engineer Knows

LLDmediumInterview freq: high
Choose your preparation mode4 modes available
🏗️
When to Use Inheritance vs Composition - The Rule Every Senior Engineer Knows
Master the Java-specific tradeoffs between inheritance and composition for extensible, maintainable designs.
mediumOOP Foundations for InterviewsFreq: high
Prerequisites:OOP FundamentalsJava concurrency basicsSOLID principles
⚙️
Functional Requirements
  • Provide base behavior with shared state using abstract classes
  • Allow multiple behavior contracts via interfaces
  • Enable composition to delegate behavior to contained objects
  • Prevent clients from modifying internal state directly
  • Ensure thread-safe state changes using synchronized methods
  • Use volatile fields to guarantee visibility of shared references
  • Support extension without modifying existing classes (Open-Closed Principle)
👤
Use Cases
  • Extend functionality of existing classes without modifying them
  • Reuse common behavior across multiple classes
  • Allow runtime behavior changes by swapping components
  • Prevent fragile base class problems in inheritance hierarchies
  • Ensure thread-safe access to shared mutable state
  • Support multiple implementations of a behavior contract
🧑‍💻
Actors
  • Senior Software Engineer
  • Junior Developer
  • Code Reviewer
  • QA Engineer
❓
Clarifying Questions to Ask
  • How many concurrent threads will access the shared objects?
  • Is there a pricing or licensing strategy affecting class design?
  • Are there edge cases where behavior must change dynamically at runtime?
  • Should the design allow multiple inheritance of behavior?
  • What is the expected lifecycle of composed objects?
  • Is thread safety required for all methods or only some?
Out of Scope
✗ UI design or frontend concerns
✗ Persistence or database integration
✗ Distributed system design
✗ Performance optimization beyond concurrency correctness
⚡
QPS Calculation
read qps1000
write qps200
🖥️
Server Estimate
3
🔢
Enumerations
enum ExtensionType
INHERITANCE | COMPOSITION
Defines the two extension mechanisms explicitly to avoid string typos and enable exhaustive switch statements in design decisions.
enum Visibility
PUBLIC | PROTECTED | PRIVATE
Models Java access modifiers to clarify encapsulation levels in class design and enforce Single Responsibility Principle.
📦
BaseComponent
2 fields
protected String componentNameprotected volatile boolean initialized
Provide common state and behavior for components that require shared mutable state and initialization logic.
Why thisUses abstract class to hold shared state and synchronized methods, which interfaces cannot provide in Java.
Why notInterfaces lack state and synchronized behavior; composition alone cannot share mutable state easily.
public synchronized void initialize()Atomically initializes the component, setting initialized flag to true with visibility guarantees.
public boolean isInitialized()Returns the volatile initialized flag to ensure visibility across threads.
📦
BehaviorContract
0 fields
Defines a behavior contract as a Java interface without state, enabling multiple implementations.
Why thisInterfaces allow multiple inheritance of behavior and decouple implementation from contract.
Why notAbstract classes cannot be multiply inherited; interfaces provide flexibility for composition.
void executeBehavior()Abstract behavior method to be implemented by concrete classes.
📦
ConcreteComponent
1 fields
private int state
Extends BaseComponent to add concrete state and behavior, demonstrating inheritance with state.
Why thisInheritance allows reuse of BaseComponent's state and synchronized methods for thread safety.
Why notComposition would require delegation and more boilerplate for shared state management.
public synchronized void incrementState()Atomically increments internal state to prevent race conditions.
public int getState()Returns current state value.
📦
CompositeComponent
1 fields
private volatile BehaviorContract delegate
Uses composition to delegate behavior to another object implementing BehaviorContract.
Why thisComposition enables runtime swapping of behavior implementations, supporting Open-Closed Principle.
Why notInheritance is static and cannot change behavior at runtime; composition is more flexible.
public void executeBehavior()Delegates behavior execution to the composed BehaviorContract instance.
public synchronized void setDelegate(BehaviorContract delegate)Atomically sets the delegate reference with volatile visibility guarantees.
📦
Client
1 fields
private BaseComponent component
Uses components via inheritance or composition without knowing internal details, enforcing Dependency Inversion Principle.
Why thisClient depends on abstractions, not concrete implementations, enabling flexibility and testability.
Why notDirect dependency on concrete classes reduces extensibility and violates DIP.
public void useComponent()Invokes component behavior polymorphically.
🔗
Relationships
ConcreteComponent──►BaseComponent[1:1]
inheritancechild destroyed with parent
CompositeComponent◆──►BehaviorContract[1:1]
compositiondelegate destroyed with composite
Client──►BaseComponent[1:1]
associationcomponent exists independently
Client──►BehaviorContract[1:1]
associationdelegate exists independently
🗄️
ComponentState
PK: componentName
componentName STRINGtimestamp TIMESTAMPstate INTinitialized BOOLEAN
Stores state and initialization status of components over time.
🗄️
BehaviorImplementations
PK: behaviorId
behaviorId STRINGversion INTimplementationDetails STRING
Tracks different versions of behavior implementations for composition.
🔌
InitializeComponent
Request
{
  "componentName": "string"
}
Response
{
  "success": "boolean",
  "message": "string"
}
Initializes a component atomically and marks it as initialized.
🔌
SetBehaviorDelegate
Request
{
  "componentName": "string",
  "behaviorId": "string"
}
Response
{
  "success": "boolean",
  "message": "string"
}
Sets the behavior delegate for a composite component safely.
🔌
GetComponentState
Request
{
  "componentName": "string"
}
Response
{
  "state": "int",
  "initialized": "boolean"
}
Retrieves current state and initialization status of a component.

Practice

(1/5)
1. In implementing a chain of handlers in Java, should the Handler interface be an abstract class or an interface, and why?
easy
A. Interface, because handlers only define a contract without shared state, enabling flexible implementations.
B. Interface, because it enforces inheritance and prevents multiple implementations.
C. Abstract class, because Java interfaces cannot have default method implementations.
D. Abstract class, because it allows sharing common state and behavior among handlers.

Solution

  1. Step 1: Understand Handler role

    Handlers define a contract to process or pass requests; they typically do not share mutable state.
  2. Step 2: Evaluate Java features

    Interfaces allow multiple inheritance and default methods (since Java 8), promoting flexibility; abstract classes share state but restrict inheritance.
  3. Final Answer:

    Option A -> Option A
  4. Quick Check:

    Interface defines contract without forcing shared state [check]
Hint: Interface for contract, abstract class for shared state [check]
Common Mistakes:
2. In the Stack Overflow design, the Vote class has a private constructor and a static synchronized getInstance() method. Which design pattern is applied here and why is this the correct use case?
easy
A. Factory pattern, because it controls object creation with parameters
B. Prototype pattern, to clone existing Vote instances for performance
C. Singleton pattern, to ensure only one Vote manager instance handles voting logic
D. Builder pattern, to incrementally construct Vote objects with complex state

Solution

  1. Step 1: Identify the pattern from constructor and method

    The private constructor prevents external instantiation, and the static synchronized getInstance() method ensures a single instance is created and accessed thread-safely.
  2. Step 2: Why Singleton fits Vote manager

    Vote management requires a single point of control to maintain consistent vote counts and prevent conflicting updates, which Singleton enforces.
  3. Final Answer:

    Option C -> Option C
  4. Quick Check:

    Private constructor + synchronized static getInstance() -> Singleton [check]
Hint: Private constructor + static synchronized getInstance() -> Singleton [check]
Common Mistakes:
3. In a multi-threaded parking lot system, if parkVehicle() is not synchronized, which exact sequence of Thread 1 and Thread 2 calls causes double-booking of the same spot?
medium
A. Thread 1 checks spot availability -> true, context switch; Thread 2 checks spot availability -> true; both park simultaneously.
B. Thread 1 parks vehicle, Thread 2 waits for Thread 1 to finish, then parks vehicle.
C. Thread 1 parks vehicle, Thread 2 parks vehicle immediately after without checking availability.
D. Thread 1 releases spot, Thread 2 parks vehicle before Thread 1 finishes updating status.

Solution

  1. Step 1: Identify race condition

    Without synchronization, both threads can read spot availability as true before either marks it occupied.
  2. Step 2: Trace exact interleaving

    Thread 1: isAvailable() -> true, context switch; Thread 2: isAvailable() -> true; both proceed to park, causing double-booking.
  3. Final Answer:

    Option A -> Option A
  4. Quick Check:

    Check-then-act gap without synchronization -> double-booking [check]
Hint: Check availability then park must be atomic to avoid double-booking [check]
Common Mistakes:
4. You want to add a WeekendFeeStrategy to calculate notification fees differently on weekends. According to SOLID principles, what is the minimum change required in the current design that uses a FeeStrategy interface?
medium
A. Create a new WeekendFeeStrategy class implementing FeeStrategy and inject it without modifying existing classes.
B. Add a boolean weekend flag to FeeStrategy interface and update all implementations accordingly.
C. Modify the existing FeeStrategy implementation to add weekend logic with if-else checks.
D. Create a WeekendFeeStrategy subclass extending the existing FeeStrategy implementation.

Solution

  1. Step 1: Identify applicable SOLID principle

    The Open-Closed Principle (OCP) requires classes to be open for extension but closed for modification.
  2. Step 2: Evaluate options against OCP

    Create a new WeekendFeeStrategy class implementing FeeStrategy and inject it without modifying existing classes. adds a new class implementing FeeStrategy without modifying existing code, adhering to OCP. Others modify existing classes or interfaces, violating OCP.
  3. Final Answer:

    Option A -> Option A
  4. Quick Check:

    New strategy class extends behavior without modifying existing code [check]
Hint: OCP: extend with new class, don't modify existing [check]
Common Mistakes:
5. In a parking system, parkVehicle() checks if a spot is available and then marks it occupied. Without synchronized, which exact thread interleaving causes double-booking?
medium
A. Thread 1 marks spot occupied, Thread 2 waits until spot is free, then parks
B. Thread 1 checks isAvailable() -> true, context switch before marking occupied; Thread 2 checks isAvailable() -> true, both mark occupied
C. Thread 1 parks vehicle, Thread 2 parks vehicle after Thread 1 completes
D. Thread 1 and Thread 2 both wait on a lock, preventing double-booking

Solution

  1. Step 1: Identify the check-then-act race condition

    Without synchronization, Thread 1 reads spot as available but is preempted before marking it occupied. Thread 2 also reads available and proceeds to mark occupied.
  2. Step 2: Confirm double-booking scenario

    Both threads believe the spot is free and mark it occupied, causing double-booking due to the race between check and set.
  3. Final Answer:

    Option B -> Option B
  4. Quick Check:

    Check-then-act gap without synchronization -> double-booking [check]
Hint: Check-then-act without lock -> race condition double-booking [check]
Common Mistakes:
Recommended

Ready to prove it?

Pass the test and this pattern moves unknown strong while earning +1 Grip toward your certificate.

10 questions~5 mineasy hard